Bläddra i källkod

Uppdatering från bärbar dator, BetSafe tillagt

Axel Nordh 3 år sedan
förälder
incheckning
8f05ca4477

+ 21 - 0
Odds/.classpath

@@ -16,5 +16,26 @@
 			<attribute name="maven.pomderived" value="true"/>
 		</attributes>
 	</classpathentry>
+	<classpathentry kind="src" path="target/generated-sources/annotations">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="ignore_optional_problems" value="true"/>
+			<attribute name="m2e-apt" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
+		<attributes>
+			<attribute name="optional" value="true"/>
+			<attribute name="maven.pomderived" value="true"/>
+			<attribute name="ignore_optional_problems" value="true"/>
+			<attribute name="m2e-apt" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="src" path=".apt_generated">
+		<attributes>
+			<attribute name="optional" value="true"/>
+		</attributes>
+	</classpathentry>
 	<classpathentry kind="output" path="target/classes"/>
 </classpath>

+ 1 - 1
Odds/.settings/org.eclipse.jdt.core.prefs

@@ -167,7 +167,7 @@ org.eclipse.jdt.core.compiler.problem.unusedTypeArgumentsForMethodInvocation=war
 org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.processAnnotations=disabled
+org.eclipse.jdt.core.compiler.processAnnotations=enabled
 org.eclipse.jdt.core.compiler.release=disabled
 org.eclipse.jdt.core.compiler.source=18
 org.eclipse.jdt.core.compiler.storeAnnotations=disabled

BIN
Odds/chromedriver.exe


+ 27 - 15
Odds/src/main/ArbChecker.java

@@ -12,27 +12,39 @@ import java.util.Arrays;
 import java.util.List;
 
 import object.ArbResults;
+import parser.BetSafeParser;
+import parser.ExpektParser;
+import parser.NordicBetParser;
 
 public class ArbChecker {
 
     private List<ArbResults> nordicBetMatches = new ArrayList<>();
     private List<ArbResults> expektResults = new ArrayList<>();
+    private List<ArbResults> betSafeResults = new ArrayList<>();
 
     public ArbChecker() {
-//        NordicBetParser nbp = new NordicBetParser();
-//        nordicBetMatches.addAll(nbp.parseSoccerMatches().stream().map(m -> new ArbResults(m, "NordicBet")).toList());
-//        printResultsToArrayFile(nordicBetMatches, "NordicBet.csv");
-//
-//        ExpektParser ep = new ExpektParser();
-//        expektResults.addAll(ep.seleniumParseSoccerMatches().stream().map(m -> new ArbResults(m, "Expekt")).toList());
-//        printResultsToArrayFile(expektResults, "Expekt.csv");
+    //    NordicBetParser nbp = new NordicBetParser();
+    //    nordicBetMatches.addAll(nbp.parseSoccerMatches().stream().map(m -> new ArbResults(m, "NordicBet")).toList());
+    //    printResultsToArrayFile(nordicBetMatches, "NordicBet.csv");
+
+    //    ExpektParser ep = new ExpektParser();
+    //    expektResults.addAll(ep.seleniumParseSoccerMatches().stream().map(m -> new ArbResults(m, "Expekt")).toList());
+    //    printResultsToArrayFile(expektResults, "Expekt.csv");
+
+    // BetSafeParser bsp = new BetSafeParser();
+    // betSafeResults.addAll(bsp.seleniumPaseSoccerMatches().stream().map(m -> new ArbResults(m, "BetSafe")).toList());
+    // printResultsToArrayFile(betSafeResults, "BetSafe.csv");
 
         getResultsFromFile("NordicBet.csv", nordicBetMatches, "NordicBet");
         getResultsFromFile("Expekt.csv", expektResults, "Expekt");
+        getResultsFromFile("BetSafe.csv", expektResults, "BetSafe");
 
         List<SimpleEntry<String, List<ArbResults>>> resultLists = new ArrayList<>();
         SimpleEntry<String, List<ArbResults>> entryNordicBet = new SimpleEntry<>("NordicBet", this.nordicBetMatches);
+        SimpleEntry<String, List<ArbResults>> betSafe = new SimpleEntry<>("BetSafe", this.betSafeResults);
+
         resultLists.add(entryNordicBet);
+        resultLists.add(betSafe);
         checkForArbs(new SimpleEntry<>("Expekt", expektResults), resultLists);
     }
 
@@ -62,16 +74,16 @@ public class ArbChecker {
                 }
 
                 if (homeTeamFound.size() == 1) {
-                    match.addOdds1(homeTeamFound.get(0).getOdds1());
-                    match.addOddsX(homeTeamFound.get(0).getOddsX());
-                    match.addOdds2(homeTeamFound.get(0).getOdds2());
+                    match.addOdds1(list.getKey(), homeTeamFound.get(0).getOdds1());
+                    match.addOddsX(list.getKey(), homeTeamFound.get(0).getOddsX());
+                    match.addOdds2(list.getKey(), homeTeamFound.get(0).getOdds2());
                     System.out.println(
                             "Found match mainlist " + match.toStringWithExtra() + " arb value "
                                     + calculateArbValue3Part(match));
                 } else if (awayTeamFound.size() == 1) {
-                    match.addOdds1(awayTeamFound.get(0).getOdds1());
-                    match.addOddsX(awayTeamFound.get(0).getOddsX());
-                    match.addOdds2(awayTeamFound.get(0).getOdds2());
+                    match.addOdds1(list.getKey(), awayTeamFound.get(0).getOdds1());
+                    match.addOddsX(list.getKey(), awayTeamFound.get(0).getOddsX());
+                    match.addOdds2(list.getKey(), awayTeamFound.get(0).getOdds2());
                     System.out.println(
                             "Found match mainlist " + match.toStringWithExtra() + " arb value "
                                     + calculateArbValue3Part(match));
@@ -81,12 +93,12 @@ public class ArbChecker {
     }
 
     private float calculateArbValue3Part(ArbResults res) {
-        return (1 / res.getMaxOdds1()) + (1 / res.getMaxOddsX()) + (1 / res.getMaxOdds2());
+        return (1 / res.getMaxOdds1().getOdds()) + (1 / res.getMaxOddsX().getOdds()) + (1 / res.getMaxOdds2().getOdds());
     }
 
     private void printResultsToArrayFile(List<ArbResults> results, String fileName) {
         try (BufferedWriter bw = new BufferedWriter(
-                new FileWriter(System.getProperty("user.dir") + File.separator + fileName))) {
+            new FileWriter(System.getProperty("user.dir") + File.separator + fileName))) {
             StringBuilder sb = new StringBuilder();
 
             for (ArbResults r : results) {

+ 29 - 10
Odds/src/object/ArbResults.java

@@ -3,6 +3,7 @@ package object;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 
 public class ArbResults extends ResultDTO {
 
@@ -25,28 +26,37 @@ public class ArbResults extends ResultDTO {
         this(m.homeTeam, m.awayTeam, m.odds1, m.oddsX, m.odds2, bookie);
     }
 
-    public void addOdds1(float value) {
+    public void addOdds1(String bookie, float value) {
         odds1List.add(new Odds(bookie, value));
     }
 
-    public void addOddsX(float value) {
+    public void addOddsX(String bookie, float value) {
         oddsXList.add(new Odds(bookie, value));
     }
 
-    public void addOdds2(float value) {
+    public void addOdds2(String bookie, float value) {
         odds2List.add(new Odds(bookie, value));
     }
 
-    public float getMaxOdds1() {
-        return Collections.max(odds1List.stream().map(m -> m.odds).toList());
+    public Odds getMaxOdds1() {
+        Float val = Collections.max(odds1List.stream().map(m -> m.odds).toList());
+        Optional<Odds> o = odds1List.stream().filter(p -> p.odds == val).findFirst();
+
+        return o.isPresent()?o.get():null;
     }
 
-    public float getMaxOddsX() {
-        return Collections.max(oddsXList.stream().map(m -> m.odds).toList());
+    public Odds getMaxOddsX() {
+        Float val = Collections.max(oddsXList.stream().map(m -> m.odds).toList());
+        Optional<Odds> o = oddsXList.stream().filter(p -> p.odds == val).findFirst();
+
+        return o.isPresent()?o.get():null;
     }
 
-    public float getMaxOdds2() {
-        return Collections.max(odds2List.stream().map(m -> m.odds).toList());
+    public Odds getMaxOdds2() {
+        Float val = Collections.max(odds2List.stream().map(m -> m.odds).toList());
+        Optional<Odds> o = odds2List.stream().filter(p -> p.odds == val).findFirst();
+
+        return o.isPresent()?o.get():null;
     }
 
     public String toStringWithExtra() {
@@ -55,7 +65,7 @@ public class ArbResults extends ResultDTO {
         return string;
     }
 
-    class Odds {
+    public class Odds {
         String bookie;
         float odds;
 
@@ -63,5 +73,14 @@ public class ArbResults extends ResultDTO {
             this.bookie = bookie;
             this.odds = odds;
         }
+
+        public float getOdds() {return odds;}
+
+        public String getBookie() {return bookie;}
+
+        @Override
+        public String toString() {
+            return "(" + bookie + ") " + odds;
+        }
     }
 }

+ 174 - 0
Odds/src/parser/BetSafeParser.java

@@ -0,0 +1,174 @@
+package parser;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.StaleElementReferenceException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+
+import object.ResultDTO;
+
+public class BetSafeParser extends ParserBase {
+
+    List<ResultDTO> result = new ArrayList<>();
+
+    public BetSafeParser() {}
+
+    public List<ResultDTO> seleniumPaseSoccerMatches() {
+        String url = "https://www.betsafe.com/sv/odds/fotboll";
+
+        ChromeDriver driver = getSeleniumDriver();
+
+        driver.manage().deleteAllCookies();
+        driver.get(url);
+        JavascriptExecutor js = driver;
+        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
+        wait.until(ExpectedConditions
+                .numberOfElementsToBeMoreThan(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"), 0));
+
+        List<WebElement> matchDivs = driver
+                .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+
+        List<WebElement> expandableDivs = driver
+                .findElements(By.xpath(
+                        "//div[contains(@class, 'obg-m-events-master-detail-header') and not(contains(@class, 'expanded'))]"));
+
+        // leta upp child span med text "kommande idag" och kanske "imorgon"
+        // Behöver också kolla om det finns mer att ladda obg-show-more-less-button
+        // ng-star-inserted
+        // Kontrollera att det inte står "Show less" då den minimerar igen
+        for (WebElement element : expandableDivs) {
+            try {
+                String xpath = ".//span[text()='Live']";
+                if (checkIfElementExists(element, xpath)) {
+                    element.click();
+                    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                            "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+                    matchDivs = driver
+                            .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+                    scrollElementIntoView(driver, element);
+                }
+
+                xpath = ".//span[text()='Kommande idag']";
+                if (checkIfElementExists(element, xpath)) {
+                    element.click();
+                    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                            "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+
+                    matchDivs = driver
+                            .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+                    scrollElementIntoView(driver, element);
+
+                }
+
+                xpath = ".//span[text()='Imorgon']";
+                if (checkIfElementExists(element, xpath)) {
+                    element.click();
+                    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                            "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+
+                    matchDivs = driver
+                            .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+                    scrollElementIntoView(driver, element);
+                }
+
+            } catch (ElementNotFoundException e) {
+                // Empty by design, no more matches today
+            }
+        }
+
+        expandAllMatches(driver, wait, matchDivs);
+
+        matchDivs = driver
+                .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+
+        js.executeScript("window.stop;");
+        for (WebElement match : matchDivs) {
+            parseMatch(driver, match);
+        }
+        System.out.println("found " + matchDivs.size() + " matches");
+
+        driver.close();
+        return result;
+    }
+
+    private void expandAllMatches(ChromeDriver driver, WebDriverWait wait, List<WebElement> matchDivs) {
+        List<WebElement> buttons = driver
+        .findElements(By.xpath("//button[contains(@class,'obg-show-more-less-button')]"));
+        scrollElementIntoViewCenter(driver, buttons.get(0));
+
+        for (WebElement button : buttons) {
+            WebElement findElement = button.findElement(By.xpath(".//span"));
+            while (!findElement.getText().contains("färre")) {
+                scrollElementIntoViewCenter(driver, buttons.get(0));
+                button.click();
+                findElement = button.findElement(By.xpath(".//span"));
+            }
+
+        }
+
+        if (buttons.size() > 0) {
+            wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                    "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+        }
+    }
+
+    private void parseMatch(ChromeDriver driver, WebElement match) {
+        // teams div, two div children with hometeam awayTeam
+        // "class obg-event-info-participant obg-event-info-participant-default-layout"
+        try {
+            List<WebElement> participants = match
+                    .findElements(By.xpath(".//div[@class='obg-event-info-participant-label ng-star-inserted']"));
+
+            String homeTeamName = participants.get(0).getAttribute("title");
+            String awayTeamName = participants.get(1).getAttribute("title");
+
+            List<WebElement> oddsContainers = match.findElements(By.xpath(".//obg-event-row-market-container"));
+
+            Optional<WebElement> matchOddsContainer = oddsContainers.stream()
+                    .filter(p -> p.findElement(By.xpath("//header/div/span"))
+                            .getText().equals("Matchresultat"))
+                    .findFirst();
+            float odds1 = -1f;
+            float oddsX = -1f;
+            float odds2 = -1f;
+            if (matchOddsContainer.isPresent()) {
+                List<WebElement> oddsValueContainers = matchOddsContainer.get()
+                        .findElements(By.xpath(".//section//span[@test-id='odds']"));
+
+                if (!oddsValueContainers.isEmpty()) {
+                    odds1 = formatFloat(oddsValueContainers.get(0).getText());
+                    oddsX = formatFloat(oddsValueContainers.get(1).getText());
+                    odds2 = formatFloat(oddsValueContainers.get(2).getText());
+                }
+
+            }
+            result.add(new ResultDTO(homeTeamName, awayTeamName, odds1, oddsX, odds2));
+            System.out.println(homeTeamName + "-" + awayTeamName + " odds (" + odds1 + "," + oddsX + "," + odds2 + ")");
+        } catch (StaleElementReferenceException e) {
+            System.out.println("Stale element, refreshing");
+            driver.navigate().refresh();
+            parseMatch(driver, match);
+        }
+    }
+
+    private float formatFloat(String string) {
+        float result = -1f;
+        try {
+            result = Float.parseFloat(string);
+        } catch (NumberFormatException e) {
+            // Empty by design
+        }
+
+        return result;
+    }
+}

+ 1 - 1
Odds/src/parser/ParserBase.java

@@ -14,7 +14,7 @@ public class ParserBase {
         ChromeOptions options = new ChromeOptions();
 
         System.setProperty("webdriver.chrome.driver",
-                System.getProperty("user.dir") + "/src/main/resources/chromedriver.exe");
+                System.getProperty("user.dir") + "/odds/Odds/chromedriver.exe");
         System.setProperty("webdriver.chrome.silentOutput", "true");
         // Fixing 255 Error crashes
         options.addArguments("--no-sandbox");