Example #1
0
        protected override async Task ScrapeData()
        {
            const string url = "https://api.neds.com.au/v2/sport/event-request?category_ids=%5B%223c34d075-dc14-436d-bfc4-9272a49c2b39%22%5D";
            var          doc = await ScrapeHelper.GetDocument(new Uri(url));

            var jDoc       = JsonConvert.DeserializeObject <JToken>(doc);
            var rawMatches = jDoc.SelectToken("$.events").ToList();

            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            foreach (var matchContent in rawMatches.Select(rawMatch => rawMatch.Children().FirstOrDefault()))
            {
                if (matchContent == null)
                {
                    Logger.Warning("Match content is null");
                    continue;
                }

                var sourceMatchId = matchContent.SelectToken("$.id").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source match id is null");
                    continue;
                }

                var matchName       = matchContent.SelectToken("$.name").ToString();
                var competitionName = matchContent.SelectToken("$.competition.name").ToString();

                if (!matchName.Contains(" V ") || !competitionName.Contains("NBA"))
                {
                    continue;
                }

                var homeTeam = ScrapeHelper.RegexMappingExpression(matchName, "(.*) V");
                var awayTeam = ScrapeHelper.RegexMappingExpression(matchName, "V (.*)");

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);

                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }
            Logger.Information("Scrape match data complete");
            await UpdateScrapeStatus(20, "Scrape match data complete");

            Logger.Information("Scraping metric data");
            await UpdateScrapeStatus(20, "Scraping metric data");

            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://api.neds.com.au/v2/sport/event-card?id={match.SourceId}";
                doc = await ScrapeHelper.GetDocument(new Uri(metricUrl));

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);

                var entrants = jDoc.SelectToken("$.entrants").ToList();
                var prices   = jDoc.SelectToken("$.prices").ToList();
                ProcessMetric(match, entrants, prices, ScoreType.Point, "Points");
                ProcessMetric(match, entrants, prices, ScoreType.Rebound, "Rebounds");
                ProcessMetric(match, entrants, prices, ScoreType.Assist, "Assists");
                ProcessMetric(match, entrants, prices, ScoreType.PointReboundAssist, "PRA");
                ProcessMetric(match, entrants, prices, ScoreType.PointRebound, "PR");
                ProcessMetric(match, entrants, prices, ScoreType.PointAssist, "PA");
                ProcessMetric(match, entrants, prices, ScoreType.ReboundAssist, "RA");

                var newProgress = GetScrapingInformation().Progress;
                newProgress = Math.Min(newProgress + 90 / foundMatches.Count, 90);
                await UpdateScrapeStatus(newProgress, null);
            }
            Logger.Information("Scrape metric data complete");
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
Example #2
0
        protected override async Task ScrapeData()
        {
            const string url = "https://ubet.com/api/sportsViewData/mainevents/false/6698";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc = JsonConvert.DeserializeObject <JToken>(doc);

            var rawMatches   = jDoc.SelectTokens("$.[*]");
            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            foreach (var rawMatch in rawMatches)
            {
                var sourceMatchId = rawMatch.SelectToken("$.MainEventId").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }

                var mainEventName = rawMatch.SelectToken("$.MainEventName").ToString();
                var date          = Helper.GetCurrentDate("dd/M");
                var teams         = mainEventName
                                    .Split(new[] { date }, StringSplitOptions.None)[0]
                                    .Split(new[] { " v " }, StringSplitOptions.None);

                var homeTeam = teams[0];
                var awayTeam = teams[1];

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }
            Logger.Information("Scrape match data complete");
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            Logger.Information("Scraping metric data");
            var betTypes = new HashSet <string>();

            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://ubet.com/api/sportsViewData/markets/false/{match.SourceId}";
                doc = await ScrapeHelper.GetDocument(metricUrl);

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);
                var rawMetrics = jDoc
                                 .SelectTokens("$.All[?(@.GroupId == -1)].SubEvents[?(@.LongDisplayName =~ /(.* (Ttl|Total).*)/)]")
                                 .ToList();

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                foreach (var rawMetric in rawMetrics)
                {
                    var betTypeShortName = rawMetric.SelectToken("$.BetTypeShortName").ToString();
                    var scoreType        =
                        betTypeShortName.Contains("Total Points Scored") ? ScoreType.Point :
                        betTypeShortName.Contains("Total Pts+Rebound+Assist") ? ScoreType.PointReboundAssist :
                        string.Empty;

                    betTypes.Add(betTypeShortName);

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }
                    var longDisplayName = rawMetric.SelectToken("$.LongDisplayName").ToString();
                    var playerName      = ScrapeHelper.RegexMappingExpression(longDisplayName, @"(.*)(?:Ttl|Total)");
                    var player          = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    double?over = 0, overLine = 0, under = 0, underLine = 0;
                    var    offers = rawMetric.SelectTokens(@"$.Offers[?(@.OfferName =~ /(.* Over|Under|OV|UN .*)/)]");
                    foreach (var offer in offers)
                    {
                        var offerName = offer.SelectToken("$.OfferName").ToString();
                        if (offerName.Contains("Over") || offerName.Contains("OV"))
                        {
                            over     = ScrapeHelper.ConvertMetric(offer.SelectToken("$.WinReturn").ToString());
                            overLine = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(offerName, @".* (\d*.\d)"));
                        }
                        else if (offerName.Contains("Under") || offerName.Contains("UN"))
                        {
                            under     = ScrapeHelper.ConvertMetric(offer.SelectToken("$.WinReturn").ToString());
                            underLine = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(offerName, @".* (\d*.\d)"));
                        }
                    }

                    Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                    var metric = new PlayerOverUnder
                    {
                        MatchId               = match.Id,
                        Over                  = over,
                        OverLine              = overLine,
                        Under                 = under,
                        UnderLine             = underLine,
                        PlayerId              = player.Id,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        CreatedAt             = DateTime.Now
                    };

                    PlayerUnderOvers.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            Logger.Information($"Unique scoreTypes: {JsonConvert.SerializeObject(betTypes)}"); // notify to update
            Logger.Information("Scrape metric data complete");
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
Example #3
0
        protected override async Task ScrapeData()
        {
            const string url = "https://services.bovada.lv/services/sports/event/coupon/events/A/description/basketball/nba?marketFilterId=def&preMatchOnly=true&lang=en";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc       = JsonConvert.DeserializeObject <JToken>(doc);
            var rawMatches = jDoc.SelectTokens("$.[*].events[*]");

            await UpdateScrapeStatus(10, "Scraping match data");

            var foundMatches = new List <Match>();

            foreach (var rawMatch in rawMatches)
            {
                var sourceId = rawMatch.SelectToken("$.link").ToString();
                if (string.IsNullOrEmpty(sourceId))
                {
                    Logger.Warning("Source match id is null");
                    continue;
                }
                var description = rawMatch.SelectToken("$.description").ToString();
                var desSplits   = description.Split('@');
                var homeTeam    = desSplits[1];
                var awayTeam    = desSplits[0];

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceId;
                foundMatches.Add(match);
            }
            await UpdateScrapeStatus(20, "Scrape match data complete");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            await UpdateScrapeStatus(20, "Scraping metric data");

            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://services.bovada.lv/services/sports/event/coupon/events/A/description{match.SourceId}?lang=en";
                doc = await ScrapeHelper.GetDocument(metricUrl);

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                var rawMetrics = jDoc
                                 .SelectTokens("$.[*].events[*].displayGroups[?(@.description == 'Player Props')].markets[*]")
                                 .ToList();
                foreach (var rawMetric in rawMetrics)
                {
                    var description = rawMetric.SelectToken("$.description").ToString();
                    var scoreType   =
                        description.Contains("Total Points, Rebounds and Assists") ? ScoreType.PointReboundAssist :
                        description.Contains("Total Points and Rebounds") ? ScoreType.PointRebound :
                        description.Contains("Total Points and Assists") ? ScoreType.PointAssist :
                        description.Contains("Total Rebounds and Assists") ? ScoreType.ReboundAssist :
                        description.Contains("Total Points") ? ScoreType.Point :
                        description.Contains("Total Rebounds") ? ScoreType.Rebound :
                        description.Contains("Total Assists") ? ScoreType.Assist :
                        string.Empty;

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var playerName = ScrapeHelper.RegexMappingExpression(description, @"- (.*) \(");
                    var player     = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    var    outcomes = rawMetric.SelectTokens("$.outcomes[*]");
                    double?over = 0, overLine = 0, under = 0, underLine = 0;
                    foreach (var outcome in outcomes)
                    {
                        var des = outcome.SelectToken("$.description").ToString();
                        if (des.Contains("Over"))
                        {
                            overLine = ScrapeHelper.ConvertMetric(outcome.SelectToken("$.price.handicap").ToString());
                            var overPrice = ScrapeHelper.ConvertMetric(outcome.SelectToken("$.price.decimal").ToString());
                            over = overPrice != null?Math.Round((double)overPrice, 2) : (double?)null;
                        }
                        else if (des.Contains("Under"))
                        {
                            underLine = ScrapeHelper.ConvertMetric(outcome.SelectToken("$.price.handicap").ToString());
                            var underPrice = ScrapeHelper.ConvertMetric(outcome.SelectToken("$.price.decimal").ToString());
                            under = underPrice != null?Math.Round((double)underPrice, 2) : (double?)null;
                        }
                    }

                    Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                    var metric = new PlayerOverUnder
                    {
                        MatchId               = match.Id,
                        Over                  = over,
                        OverLine              = overLine,
                        Under                 = under,
                        UnderLine             = underLine,
                        PlayerId              = player.Id,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        CreatedAt             = DateTime.Now
                    };

                    PlayerUnderOvers.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
Example #4
0
        protected override async Task ScrapeData()
        {
            const string url = "https://cds-api.borgataonline.com/bettingoffer/fixtures?x-bwin-accessid=ZTJhZDliYjgtNTdmOC00Njk0LWIxZmItODI3YzhjZGQ5NmIx&lang=en-US&country=VN&userCountry=VN&streamProviders=unas%2Cperform%2Cimgdge&fixtureTypes=Standard&state=Latest&skip=0&take=50&offerMapping=Filtered&offerCategories=Gridable&fixtureCategories=Gridable,NonGridable,Other&sortBy=Tags&sportIds=7&regionIds=9&competitionIds=6004";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc       = JsonConvert.DeserializeObject <JToken>(doc);
            var rawMatches = jDoc.SelectTokens("$.fixtures[*]");

            await UpdateScrapeStatus(10, "Scraping match data");

            var foundMatches = new List <Match>();

            foreach (var rawMatch in rawMatches)
            {
                var sourceId = rawMatch.SelectToken("$.id").ToString();
                if (string.IsNullOrEmpty(sourceId))
                {
                    Logger.Warning("Source match id is null");
                    continue;
                }
                var participants = rawMatch.SelectTokens("$.participants[*]").ToList();
                var homeTeam     = participants.Last().SelectToken("$.name.value").ToString();
                var awayTeam     = participants.First().SelectToken("$.name.value").ToString();

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceId;
                foundMatches.Add(match);
            }
            await UpdateScrapeStatus(20, "Scrape match data complete");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            await UpdateScrapeStatus(20, "Scraping metric data");

            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://cds-api.borgataonline.com/bettingoffer/fixture-view?x-bwin-accessid=ZTJhZDliYjgtNTdmOC00Njk0LWIxZmItODI3YzhjZGQ5NmIx&lang=en-US&country=VN&userCountry=VN&streamProviders=unas%2Cperform%2Cimgdge&offerMapping=All&scoreboardMode=Full&fixtureIds={match.SourceId}";
                doc = await ScrapeHelper.GetDocument(metricUrl);

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                var rawMetrics = jDoc.SelectTokens("$.fixture.games[*]").ToList();
                foreach (var rawMetric in rawMetrics)
                {
                    var nameData  = rawMetric.SelectToken("$.name.value").ToString();
                    var scoreType =
                        nameData.Contains("How many points") ? ScoreType.Point :
                        nameData.Contains("How many assists") ? ScoreType.Assist :
                        nameData.Contains("How many rebounds") ? ScoreType.Rebound :
                        string.Empty;
                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var playerName = ScrapeHelper.RegexMappingExpression(nameData, @".* will (.*) \(");
                    var player     = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    var overData     = rawMetric.SelectToken("$.results[0]");
                    var over         = ScrapeHelper.ConvertMetric(overData.SelectToken("$.odds").ToString());
                    var overLineData = overData.SelectToken("$.name.value").ToString();
                    var overLine     = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(overLineData, "Over (.*)"));

                    var underData     = rawMetric.SelectToken("$.results[0]");
                    var under         = ScrapeHelper.ConvertMetric(underData.SelectToken("$.odds").ToString());
                    var underLineData = underData.SelectToken("$.name.value").ToString();
                    var underLine     = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(underLineData, "Over (.*)"));

                    Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                    var metric = new PlayerOverUnder
                    {
                        MatchId               = match.Id,
                        Over                  = over,
                        OverLine              = overLine,
                        Under                 = under,
                        UnderLine             = underLine,
                        PlayerId              = player.Id,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        CreatedAt             = DateTime.Now
                    };

                    PlayerUnderOvers.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            const string url = "https://api.pointsbet.com/api/v2/competitions/7176/events/featured?includeLive=true";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc         = JsonConvert.DeserializeObject <JToken>(doc);
            var rawMatches   = jDoc.SelectTokens("$.events[*]");
            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            foreach (var rawMatch in rawMatches)
            {
                var sourceId = rawMatch.SelectToken("$.key").ToString();
                if (string.IsNullOrEmpty(sourceId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }
                var homeTeam = rawMatch.SelectToken("$.homeTeam").ToString();
                var awayTeam = rawMatch.SelectToken("$.awayTeam").ToString();
                var match    = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);

                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceId;
                foundMatches.Add(match);
            }
            Logger.Information("Scrape match data complete");
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            Logger.Information("Scrape metric data");
            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://api.pointsbet.com/api/v2/events/{match.SourceId}";
                doc = await ScrapeHelper.GetDocument(metricUrl);

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                var rawMetrics = jDoc
                                 .SelectTokens(
                    @"$.fixedOddsMarkets[?(@.name =~ /(Player Points Over\/Under .*)/ || @.name =~ /(Player Rebounds Over\/Under .*)/ || @.name =~ /(Player Assists Over\/Under .*)/)]")
                                 .ToList();
                foreach (var rawMetric in rawMetrics)
                {
                    var metricName = rawMetric.SelectToken("$.name").ToString();
                    var scoreType  =
                        metricName.Contains("Player Points") ? ScoreType.Point :
                        metricName.Contains("Player Assists") ? ScoreType.Assist :
                        metricName.Contains("Player Rebounds") ? ScoreType.Rebound :
                        metricName.Contains("Player Pts + Rebs + Asts") ? ScoreType.PointReboundAssist :
                        string.Empty;

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var outcomeOvers  = rawMetric.SelectTokens("$.outcomes[?(@.name =~ /(.* Over .*)/)]").ToList();
                    var outcomeUnders = rawMetric.SelectTokens("$.outcomes[?(@.name =~ /(.* Under .*)/)]").ToList();

                    var playerNames = new HashSet <string>();

                    foreach (var name in outcomeOvers.Select(outcome => outcome.SelectToken("name").ToString()))
                    {
                        playerNames.Add(ScrapeHelper.RegexMappingExpression(name, "(.*)(?:Over|Under)"));
                    }

                    foreach (var name in outcomeUnders.Select(outcome => outcome.SelectToken("name").ToString()))
                    {
                        playerNames.Add(ScrapeHelper.RegexMappingExpression(name, "(.*)(?:Over|Under)"));
                    }

                    foreach (var playerName in playerNames)
                    {
                        var player = ScrapeHelper.FindPlayerInMatch(playerName, match);
                        if (player == null)
                        {
                            Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                            continue;
                        }

                        var outcomeOver = outcomeOvers.FirstOrDefault(x =>
                                                                      x.SelectToken("$.name").ToString().Contains(playerName));
                        var outcomeUnder = outcomeUnders.FirstOrDefault(x =>
                                                                        x.SelectToken("$.name").ToString().Contains(playerName));

                        var rawOver     = outcomeOver?.SelectToken("price").ToString();
                        var rawOverLine =
                            ScrapeHelper.RegexMappingExpression(outcomeOver?.SelectToken("$.name").ToString(),
                                                                "(?:Over) (.*)");

                        var rawUnder     = outcomeUnder?.SelectToken("price").ToString();
                        var rawUnderLine =
                            ScrapeHelper.RegexMappingExpression(outcomeUnder?.SelectToken("$.name").ToString(),
                                                                "(?:Under) (.*)");

                        Logger.Information($"{player.Name}: {scoreType} - {rawOver} {rawOverLine} | {rawUnder} {rawUnderLine}");

                        var metric = new PlayerOverUnder
                        {
                            MatchId               = match.Id,
                            Over                  = ScrapeHelper.ConvertMetric(rawOver),
                            OverLine              = ScrapeHelper.ConvertMetric(rawOverLine),
                            Under                 = ScrapeHelper.ConvertMetric(rawUnder),
                            UnderLine             = ScrapeHelper.ConvertMetric(rawUnderLine),
                            PlayerId              = player.Id,
                            ScoreType             = scoreType,
                            ScrapingInformationId = GetScrapingInformation().Id,
                            CreatedAt             = DateTime.Now
                        };

                        PlayerUnderOvers.Add(metric);

                        var newProgress = GetScrapingInformation().Progress;
                        newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                        await UpdateScrapeStatus(newProgress, null);
                    }
                    await UpdateScrapeStatus(currentRange, null);
                }
            }
            Logger.Information("Scrape metric data complete");
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            const string url = "https://eu-offering.kambicdn.org/offering/v2018/888/listView/basketball.json?lang=en_GB&market=ZZ&client_id=2&channel_id=1&ncid=1572482733163&useCombined=true";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc       = JsonConvert.DeserializeObject <JToken>(doc);
            var rawMatches = jDoc.SelectTokens("$.events[?(@.event.group == 'NBA')]");

            await UpdateScrapeStatus(10, "Scraping match data");

            var foundMatches = new List <Match>();

            foreach (var rawMatch in rawMatches)
            {
                var sourceId = rawMatch.SelectToken("$.event.id").ToString();
                if (string.IsNullOrEmpty(sourceId))
                {
                    Logger.Warning("Source match id is null");
                    continue;
                }

                var homeTeam = rawMatch.SelectToken("$.event.homeName").ToString();
                var awayTeam = rawMatch.SelectToken("$.event.awayName").ToString();

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceId;
                foundMatches.Add(match);
            }
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://eu-offering.kambicdn.org/offering/v2018/888/betoffer/event/{match.SourceId}.json?lang=en_GB&market=ZZ&client_id=2&channel_id=1&ncid=1005710980&includeParticipants=true";
                doc = await ScrapeHelper.GetDocument(metricUrl);

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                var rawMetrics = jDoc.SelectTokens("$.betOffers[*]").ToList();
                foreach (var rawMetric in rawMetrics)
                {
                    var labelData = rawMetric.SelectToken("$.criterion.label").ToString();
                    var scoreType = labelData.Contains("Points scored by the player") ? ScoreType.Point :
                                    labelData.Contains("Rebounds by the player") ? ScoreType.Rebound :
                                    labelData.Contains("Assists by the player") ? ScoreType.Assist :
                                    labelData.Contains("Points, rebounds & assists by the player") ? ScoreType.PointReboundAssist :
                                    labelData.Contains("3-point field goals made by the player") ? ScoreType.ThreePoint :
                                    string.Empty;

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var overData  = rawMetric.SelectToken("$.outcomes[?(@.label =~ /(Over.*)/)]");
                    var underData = rawMetric.SelectToken("$.outcomes[?(@.label =~ /(Under.*)/)]");

                    var playerData   = overData.SelectToken("$.participant").ToString();
                    var playerSplits = playerData.Split(',');
                    var rvPlayer     = playerSplits.Reverse().ToList();
                    var playerName   = string.Join(" ", rvPlayer);

                    var player = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    var overLineData = overData.SelectToken("$.label").ToString();
                    var overLine     = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(overLineData, @"Over.* (\d*.\d*)"));
                    var overOdd      = ScrapeHelper.ConvertMetric(overData.SelectToken("$.odds").ToString());
                    var over         = overOdd / 1000;

                    var underLineData = underData.SelectToken("$.label").ToString();
                    var underLine     = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(underLineData, @"Under.* (\d*.\d*)"));
                    var underOdd      = ScrapeHelper.ConvertMetric(underData.SelectToken("$.odds").ToString());
                    var under         = underOdd / 1000;

                    Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                    var metric = new PlayerOverUnder
                    {
                        MatchId               = match.Id,
                        Over                  = over,
                        OverLine              = overLine,
                        Under                 = under,
                        UnderLine             = underLine,
                        PlayerId              = player.Id,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        CreatedAt             = DateTime.Now
                    };

                    PlayerUnderOvers.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            const string url    = "https://www.sportsbet.com.au/apigw/sportsbook-sports/Sportsbook/Sports/Competitions/6927/Events";
            var          rawDoc = await ScrapeHelper.GetDocument(url);

            var jDoc = JsonConvert.DeserializeObject <JToken>(rawDoc);

            var rawMatches   = jDoc.SelectTokens("$.[*]");
            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            foreach (var rawMatch in rawMatches)
            {
                if (rawMatch.SelectToken("$.competitionName").ToString() != "NBA Matches")
                {
                    continue;
                }

                var sourceMatchId = rawMatch.SelectToken("$.id").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }

                var matchName = rawMatch.SelectToken("$.name").ToString();
                var teams     = matchName.Split(new[] { " At " }, StringSplitOptions.None);
                var homeTeam  = teams[1];
                var awayTeam  = teams[0];

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }
            Logger.Information("Scrape match data complete");
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            Logger.Information("Scrape metric data");
            foreach (var match in foundMatches)
            {
                var pointsUrl      = $"https://www.sportsbet.com.au/apigw/sportsbook-sports/Sportsbook/Sports/Events/{match.SourceId}/MarketGroupings/567/Markets";
                var reboundsUrl    = $"https://www.sportsbet.com.au/apigw/sportsbook-sports/Sportsbook/Sports/Events/{match.SourceId}/MarketGroupings/568/Markets";
                var assistsUrl     = $"https://www.sportsbet.com.au/apigw/sportsbook-sports/Sportsbook/Sports/Events/{match.SourceId}/MarketGroupings/569/Markets";
                var combinationUrl = $"https://www.sportsbet.com.au/apigw/sportsbook-sports/Sportsbook/Sports/Events/{match.SourceId}/MarketGroupings/570/Markets";

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                var docTasks = new List <Task <string> >
                {
                    ScrapeHelper.GetDocument(pointsUrl),
                    ScrapeHelper.GetDocument(reboundsUrl),
                    ScrapeHelper.GetDocument(assistsUrl),
                    ScrapeHelper.GetDocument(combinationUrl)
                };

                var results = await Task.WhenAll(docTasks);

                var rawMetrics = new List <JToken>();
                foreach (var result in results)
                {
                    rawMetrics.AddRange(JsonConvert.DeserializeObject <JToken>(result).SelectTokens("$.[*]"));
                }

                foreach (var rawMetric in rawMetrics)
                {
                    var metricName = rawMetric.SelectToken("$.name").ToString();
                    var scoreType  =
                        metricName.Contains("Pts + Reb + Ast") ? ScoreType.PointReboundAssist :
                        metricName.Contains("Points") ? ScoreType.Point :
                        metricName.Contains("Assists") ? ScoreType.Assist :
                        metricName.Contains("Made Threes") ? ScoreType.ThreePoint :
                        metricName.Contains("Rebounds") ? ScoreType.Rebound :
                        metricName.Contains("Pts + Reb") ? ScoreType.PointRebound :
                        metricName.Contains("Pts + Ast") ? ScoreType.PointAssist :
                        metricName.Contains("Reb + Ast") ? ScoreType.ReboundAssist :
                        string.Empty;

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var playerName = metricName.Split('-')[0];
                    var player     = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    var    selection = rawMetric.SelectToken("$.selections[0]");
                    var    selectionName = selection.SelectToken("$.name").ToString();
                    double?over = 0, overLine = 0, under = 0, underLine = 0;
                    if (selectionName.Contains("Over"))
                    {
                        over     = ScrapeHelper.ConvertMetric(selection.SelectToken("$.price.winPrice")?.ToString());
                        overLine = ScrapeHelper.ConvertMetric(selection.SelectToken("$.unformattedHandicap")?.ToString());
                    }
                    else if (selectionName.Contains("Under"))
                    {
                        under     = ScrapeHelper.ConvertMetric(selection.SelectToken("$.price.winPrice")?.ToString());
                        underLine = ScrapeHelper.ConvertMetric(selection.SelectToken("$.unformattedHandicap")?.ToString());
                    }

                    Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                    var metric = new PlayerOverUnder
                    {
                        PlayerId              = player.Id,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        MatchId   = match.Id,
                        Over      = over,
                        OverLine  = overLine,
                        Under     = under,
                        UnderLine = underLine,
                        CreatedAt = DateTime.Now
                    };

                    PlayerUnderOvers.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            Logger.Information("Scrape metric data complete");
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            const string url    = "https://www.betvictor.com/api/top_bets/601600?number_to_show=20&exclude_in_running=true&event_ids_to_exclude=&sport_ids_to_exclude=";
            var          rawDoc = await ScrapeHelper.GetDocument(url);

            var jDoc = JsonConvert.DeserializeObject <JToken>(rawDoc);

            await UpdateScrapeStatus(10, "Scraping match data");

            var rawMatches   = jDoc.SelectTokens("$.[?(@.meeting_description == 'NBA')]");
            var foundMatches = new List <Match>();

            foreach (var rawMatch in rawMatches)
            {
                var sourceMatchId = rawMatch.SelectToken("$.event_id").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }

                if (foundMatches.Select(x => x.SourceId).Contains(sourceMatchId))
                {
                    continue;
                }

                var game     = rawMatch.SelectToken("$.event_description").ToString();
                var clubs    = game.Split('@');
                var homeTeam = clubs[1];
                var awayTeam = clubs[0];

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }
            await UpdateScrapeStatus(20, "Scrape match data complete");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            await UpdateScrapeStatus(20, "Scraping metric data");

            foreach (var match in foundMatches)
            {
                rawDoc = await ScrapeHelper.GetDocument($"https://www.betvictor.com/bv_event_level/en-gb/1/coupons/{match.SourceId}/4787?t=1573192805056");

                var jResult = JsonConvert.DeserializeObject <JToken>(rawDoc);

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                var rawMetrics = jResult.SelectTokens("$.[*].markets[*]").ToList();
                foreach (var rawMetric in rawMetrics)
                {
                    var description = rawMetric.SelectToken("$.des").ToString();
                    var scoreType   =
                        description.Contains("Total combined points, assists & rebounds") ? ScoreType.PointReboundAssist :
                        description.Contains("Total points") ? ScoreType.Point :
                        description.Contains("Total assists") ? ScoreType.Assist :
                        description.Contains("Total three pointers") ? ScoreType.ThreePoint :
                        description.Contains("Total rebounds") ? ScoreType.Rebound :
                        description.Contains("Total combined points & rebounds") ? ScoreType.PointRebound :
                        description.Contains("Total combined points & assists") ? ScoreType.PointAssist :
                        description.Contains("Total combined assists & rebounds") ? ScoreType.ReboundAssist :
                        string.Empty;

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var playerName = rawMetric.SelectToken("$.opponentDescription").ToString();
                    var player     = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    var playerMetrics = rawMetric.SelectTokens("$.o[*]");

                    double?over = 0, overLine = 0, under = 0, underLine = 0;
                    foreach (var metricItem in playerMetrics)
                    {
                        var des = metricItem.SelectToken("$.des").ToString();
                        if (des.Contains("Over"))
                        {
                            overLine = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(des, "Over (.*)"));
                            over     = ScrapeHelper.ConvertMetric(metricItem.SelectToken("$.pr").ToString());
                        }
                        else if (des.Contains("Under"))
                        {
                            underLine = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(des, "Under (.*)"));
                            under     = ScrapeHelper.ConvertMetric(metricItem.SelectToken("$.pr").ToString());
                        }
                    }

                    Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                    var metric = new PlayerOverUnder
                    {
                        PlayerId              = player.Id,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        MatchId   = match.Id,
                        Over      = over,
                        OverLine  = overLine,
                        Under     = under,
                        UnderLine = underLine,
                        CreatedAt = DateTime.Now
                    };

                    PlayerUnderOvers.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            // Fetch all teams from web portal
            var teams = await WebPortalHelper.GetTeams(Helper.GetSportCode());

            if (teams == null || teams.Count == 0)
            {
                throw new ArgumentNullException(nameof(teams), "Teams from web portal is null");
            }
            FromDate = Helper.ToMinTime(FromDate);
            ToDate   = Helper.ToMaxTime(ToDate);
            var totalDays = Convert.ToInt32((ToDate - FromDate).TotalDays);

            for (var date = FromDate.Date; date <= ToDate.Date; date = date.AddDays(1))
            {
                Logger.Information($"Scrape matches: {date.ToShortDateString()}");
                await UpdateScrapeStatus(null, $"Scrape matches: {date.ToShortDateString()}");

                var url       = $"http://site.api.espn.com/apis/site/v2/sports/basketball/nba/scoreboard?lang=en&region=au&calendartype=blacklist&limit=100&dates={Helper.GetDate(date)}&tz=Australia%2FMelbourne";
                var rawResult = await ScrapeHelper.GetDocument(url);

                var jObject = JObject.Parse(rawResult);
                if (jObject == null)
                {
                    throw new ArgumentNullException(nameof(jObject));
                }

                var sportCode    = Helper.GetSportCode();
                var teamCodes    = new List <string>();
                var competitions = jObject.SelectTokens("$.events[*].competitions[*]");
                foreach (var competition in competitions)
                {
                    var homeAbbr = competition.SelectToken("$.competitors[0].team.abbreviation").ToString();
                    var awayAbbr = competition.SelectToken("$.competitors[1].team.abbreviation").ToString();

                    if (teams.All(x => x.ShortName != homeAbbr))
                    {
                        throw new Exception($"Cannot find any home team named '{homeAbbr}' in teams");
                    }
                    if (teams.All(x => x.ShortName != awayAbbr))
                    {
                        throw new Exception($"Cannot find any away team named '{awayAbbr}' in teams");
                    }

                    teamCodes.Add(homeAbbr);
                    teamCodes.Add(awayAbbr);

                    var homeTeamId = teams.First(x => x.ShortName.Equals(homeAbbr)).Id;
                    var awayTeamId = teams.First(x => x.ShortName.Equals(awayAbbr)).Id;

                    var homeTeamName = competition.SelectToken("$.competitors[0].team.name").ToString();
                    var awayTeamName = competition.SelectToken("$.competitors[1].team.name").ToString();
                    homeTeamName = homeTeamName.Substring(0, Math.Min(homeTeamName.Length, 3)).ToUpper();
                    awayTeamName = awayTeamName.Substring(0, Math.Min(awayTeamName.Length, 3)).ToUpper();

                    var gameCode = $"{Helper.GetDate(date, "MMddyyyy")}{homeTeamName}{awayTeamName}";
                    var gameDate = competition.SelectToken("$.date").ToString();
                    Logger.Information($"Match: {gameCode}, {homeTeamName} vs {awayTeamName}, {gameDate}");
                    Matches.Add(new Match
                    {
                        StartTime  = DateTime.Parse(gameDate),
                        HomeTeamId = homeTeamId,
                        AwayTeamId = awayTeamId,
                        GameCode   = gameCode,
                        SportCode  = sportCode
                    });
                }
                Logger.Information("Scraped matches complete");

                Logger.Information("Scrape players from teams");
                const string baseTeamsUrl   = "https://www.espn.com/nba/team/stats/_/name";
                const string xPathToPlayers = "/html/body/div[1]/div/div/div/div/div[5]/div[2]/div[5]/div[1]/div/section/div/section[1]/div[2]/table/tbody/tr[*]/td/span/a";
                var          playerTasks    = new List <Task <HtmlNodeCollection> >();
                foreach (var teamCode in teamCodes)
                {
                    url = $"{baseTeamsUrl}/{teamCode}";
                    playerTasks.Add(ScrapeHelper.GetInnerHtml(url, xPathToPlayers));
                }

                var nodes = await Task.WhenAll(playerTasks);

                for (var i = 0; i < teamCodes.Count; i++)
                {
                    var teamId = teams.First(x => x.ShortName == teamCodes[i]).Id;
                    Logger.Information($"Scrape player from: {baseTeamsUrl}/{teamCodes[i]}");
                    ExtractPlayers(nodes[i], teamId);
                }

                Logger.Information("Scrape players complete");
                var newProgress = GetScrapingInformation().Progress;
                newProgress = Math.Min(newProgress + 90 / totalDays, 90);
                await UpdateScrapeStatus(newProgress, $"Scrape matches: {date.ToShortDateString()} complete");
            }
        }
Example #10
0
        protected override async Task ScrapeData()
        {
            const string url = "https://api.beta.tab.com.au/v1/tab-info-service/sports/Basketball/competitions/NBA/matches?jurisdiction=VIC";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc       = JsonConvert.DeserializeObject <JToken>(doc);
            var rawMatches = jDoc.SelectTokens("$.matches[*]");

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            var foundMatches = new List <Match>();

            foreach (var rawMatch in rawMatches)
            {
                var sourceId = rawMatch.SelectToken("$._links.self").ToString();
                if (string.IsNullOrEmpty(sourceId))
                {
                    Logger.Warning("Source match id is null");
                    continue;
                }

                var matchName = rawMatch.SelectToken("$.name").ToString();

                var homeTeam = ScrapeHelper.RegexMappingExpression(matchName, "(.*) v .*");
                var awayTeam = ScrapeHelper.RegexMappingExpression(matchName, ".* v (.*)");

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceId;
                foundMatches.Add(match);
            }
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            Logger.Information("Scrape metric data");
            foreach (var match in foundMatches)
            {
                doc = await ScrapeHelper.GetDocument($"{match.SourceId}");

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);
                var rawMetrics = jDoc.SelectTokens("$.markets[?(@.betOptionSpectrumId =~ /(837|838|839|1789|1791)/)]").ToList();

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                foreach (var rawMetric in rawMetrics)
                {
                    var spectrumId = rawMetric.SelectToken("$.betOptionSpectrumId").ToString();
                    var scoreType  = spectrumId == "837" ? ScoreType.Point :
                                     spectrumId == "838" ? ScoreType.Rebound :
                                     spectrumId == "839" ? ScoreType.Assist :
                                     spectrumId == "1789" ? ScoreType.PointReboundAssist :
                                     spectrumId == "1791" ? ScoreType.ThreePoint :
                                     string.Empty;

                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var playerOvers  = rawMetric.SelectTokens("$.propositions[?(@.name =~ /(.* Over .*)/)]").ToList();
                    var playerUnders = rawMetric.SelectTokens("$.propositions[?(@.name =~ /(.* Under .*)/)]").ToList();

                    var playerNames = new HashSet <string>();
                    foreach (var playerName in playerOvers.Select(item => item.SelectToken("name").ToString()).Select(itemName => ScrapeHelper.RegexMappingExpression(itemName, "(.*)(?:Over|Under)")))
                    {
                        playerNames.Add(playerName);
                    }

                    foreach (var playerName in playerUnders.Select(item => item.SelectToken("name").ToString()).Select(itemName => ScrapeHelper.RegexMappingExpression(itemName, "(.*)(?:Over|Under)")))
                    {
                        playerNames.Add(playerName);
                    }

                    foreach (var playerName in playerNames)
                    {
                        var player = ScrapeHelper.FindPlayerInMatch(playerName, match);
                        if (player == null)
                        {
                            Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                            continue;
                        }
                        var overOutcomeItem = playerOvers.FirstOrDefault(x => x.SelectToken("$.name").ToString().Contains(playerName));
                        var overName        = overOutcomeItem?.SelectToken("name").ToString() ?? string.Empty;
                        var overLine        = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(overName, @".* (\d*.\d*) .*"));
                        var over            = ScrapeHelper.ConvertMetric(overOutcomeItem?.SelectToken("returnWin").ToString() ?? string.Empty);

                        var underOutcomeItem = playerUnders.FirstOrDefault(x => x.SelectToken("$.name").ToString().Contains(playerName));
                        var underName        = underOutcomeItem?.SelectToken("name").ToString() ?? string.Empty;
                        var underLine        = ScrapeHelper.ConvertMetric(ScrapeHelper.RegexMappingExpression(underName, @".* (\d*.\d*) .*"));
                        var under            = ScrapeHelper.ConvertMetric(underOutcomeItem?.SelectToken("returnWin").ToString() ?? string.Empty);

                        Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                        var metric = new PlayerOverUnder
                        {
                            MatchId               = match.Id,
                            Over                  = over,
                            OverLine              = overLine,
                            Under                 = under,
                            UnderLine             = underLine,
                            PlayerId              = player.Id,
                            ScoreType             = scoreType,
                            ScrapingInformationId = GetScrapingInformation().Id,
                            CreatedAt             = DateTime.Now
                        };

                        PlayerUnderOvers.Add(metric);
                    }

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            Logger.Information("Scrape metric data complete");
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            const string url       = "https://fixture.palmerbet.online/fixtures/sports/1c2eeb3a-6bab-4ac2-b434-165cc350180f/matches?pageSize=25";
            var          rawResult = await ScrapeHelper.GetDocument(url);

            var matchObj = JsonConvert.DeserializeObject <JToken>(rawResult);

            var rawMatches   = matchObj.SelectTokens("$.matches[*]");
            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            foreach (var rawMatch in rawMatches)
            {
                var sourceMatchId = rawMatch.SelectToken("$.eventId").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }

                var homeTeam = rawMatch.SelectToken("$.homeTeam.title").ToString();
                var awayTeam = rawMatch.SelectToken("$.awayTeam.title").ToString();
                var match    = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }

            await UpdateScrapeStatus(20, "Scrape match data complete");

            var rawMetricsTasks = foundMatches
                                  .Select(match => $"https://fixture.palmerbet.online/fixtures/sports/matches/{match.SourceId}/markets?pageSize=1000")
                                  .Select(metricUrl => ScrapeHelper.GetDocument(metricUrl))
                                  .ToList();

            var rawMetrics = await Task.WhenAll(rawMetricsTasks);

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = rawMetrics.Length != 0 ? 90 / rawMetrics.Length : 0;
            var currentRange  = 20;

            Logger.Information($"Total raw metrics count {rawMetrics.Length}");
            var totalMetrics = 0;

            try
            {
                for (var i = 0; i < rawMetrics.Length; i++)
                {
                    var rawMetric     = rawMetrics[i];
                    var metricObj     = JsonConvert.DeserializeObject <JToken>(rawMetric);
                    var metricMarkets = metricObj.SelectTokens("$.markets[?(@.title=~ /(.* - .*)/)]").ToList();

                    currentRange = Math.Min(currentRange + rangeProgress, 90);

                    foreach (var metricMarket in metricMarkets)
                    {
                        var title = metricMarket.SelectToken("$.title").ToString();

                        var scoreType =
                            title.Contains("Points") ? ScoreType.Point :
                            title.Contains("Rebounds") ? ScoreType.Rebound :
                            title.Contains("Assists") ? ScoreType.Assist :
                            title.Contains("Made Threes") ? ScoreType.ThreePoint :
                            title.Contains("Pts + Ast") ? ScoreType.PointAssist :
                            title.Contains("Pts + Reb + Ast") ? ScoreType.PointReboundAssist :
                            title.Contains("Pts + Reb") ? ScoreType.PointRebound :
                            title.Contains("Reb + Ast") ? ScoreType.ReboundAssist :
                            string.Empty;

                        if (string.IsNullOrEmpty(scoreType))
                        {
                            continue;
                        }
                        var playerName = title.Split('-')[0].Trim();
                        var player     = ScrapeHelper.FindPlayerInMatch(playerName, foundMatches[i]);
                        if (player == null)
                        {
                            Logger.Warning($"Cannot find any player {playerName} in match {foundMatches[i].Id}");
                            continue;
                        }

                        totalMetrics++;

                        var playerId = metricMarket.SelectToken("$.id").ToString();
                        var priceUrl = $"https://fixture.palmerbet.online/fixtures/sports/markets/{playerId}";
                        var priceDoc = await ScrapeHelper.GetDocument(priceUrl, 3000);

                        var priceObj = JsonConvert.DeserializeObject <JToken>(priceDoc);

                        if (priceObj == null)
                        {
                            Logger.Warning($"priceObj is null for player: {playerId}");
                            continue;
                        }

                        var    outcomeItems = priceObj.SelectTokens("$.market.outcomes[*]");
                        double?over = 0, overLine = 0, under = 0, underLine = 0;
                        foreach (var outcomeItem in outcomeItems)
                        {
                            var titleItem = outcomeItem.SelectToken("$.title").ToString();

                            if (titleItem.Contains("Over"))
                            {
                                var rawOver  = ScrapeHelper.RegexMappingExpression(titleItem, "(?:Over) (.*)");
                                var rawPrice = outcomeItem.SelectTokens("$.prices[*]").FirstOrDefault()
                                               .SelectToken("$.priceSnapshot.current").ToString();

                                overLine = ScrapeHelper.ConvertMetric(rawOver);
                                over     = ScrapeHelper.ConvertMetric(rawPrice);
                            }
                            else if (titleItem.Contains("Under"))
                            {
                                var rawUnder = ScrapeHelper.RegexMappingExpression(titleItem, "(?:Under) (.*)");
                                var rawPrice = outcomeItem.SelectTokens("$.prices[*]").FirstOrDefault()
                                               .SelectToken("$.priceSnapshot.current").ToString();

                                underLine = ScrapeHelper.ConvertMetric(rawUnder);
                                under     = ScrapeHelper.ConvertMetric(rawPrice);
                            }
                        }

                        Logger.Information($"{player.Name}: {scoreType} - {over} {overLine} | {under} {underLine}");

                        var metric = new PlayerOverUnder
                        {
                            PlayerId              = player.Id,
                            ScoreType             = scoreType,
                            ScrapingInformationId = GetScrapingInformation().Id,
                            MatchId   = foundMatches[i].Id,
                            Over      = over,
                            OverLine  = overLine,
                            Under     = under,
                            UnderLine = underLine,
                            CreatedAt = DateTime.Now
                        };

                        PlayerUnderOvers.Add(metric);

                        var newProgress = GetScrapingInformation().Progress;
                        newProgress = Math.Min(newProgress + currentRange / metricMarkets.Count, currentRange);
                        await UpdateScrapeStatus(newProgress, null);
                    }

                    await UpdateScrapeStatus(currentRange, null);
                }
                await UpdateScrapeStatus(90, "Scrape metric data complete");

                Logger.Information($"Total metric count: {totalMetrics}");
            }
            catch (Exception ex)
            {
                Logger.Error(ex.ToString());
                throw;
            }
        }
        protected override async Task ScrapeData()
        {
            const string url = "https://api.beta.tab.com.au/v1/tab-info-service/sports/Basketball/competitions/NBA/matches?jurisdiction=VIC";
            var          doc = await ScrapeHelper.GetDocument(url);

            var jDoc = JsonConvert.DeserializeObject <JToken>(doc);

            var rawMatches   = jDoc.SelectTokens("$.matches[*]");
            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            foreach (var rawMatch in rawMatches)
            {
                var sourceMatchId = rawMatch.SelectToken("$._links.self").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }

                var homeTeam = rawMatch.SelectTokens("$.contestants[?(@.position == 'HOME')].name")?.FirstOrDefault()?.ToString();
                var awayTeam = rawMatch.SelectTokens("$.contestants[?(@.position == 'AWAY')].name")?.FirstOrDefault()?.ToString();

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }
            Logger.Information("Scrape match data complete");
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            Logger.Information("Scraping metric data");
            foreach (var match in foundMatches)
            {
                doc = await ScrapeHelper.GetDocument(match.SourceId);

                jDoc = JsonConvert.DeserializeObject <JToken>(doc);
                var rawMetrics = jDoc.SelectTokens("$.markets[?(@.betOptionSpectrumId =~ /(605)/)]").ToList();

                currentRange = Math.Min(currentRange + rangeProgress, 90);

                foreach (var rawMetric in rawMetrics)
                {
                    var propositionPlayerA = rawMetric.SelectToken("$.propositions[0]");
                    var propositionTie     = rawMetric.SelectToken("$.propositions[1]");
                    var propositionPlayerB = rawMetric.SelectToken("$.propositions[2]");

                    var playerNameA = ScrapeHelper.RegexMappingExpression(propositionPlayerA.SelectToken("$.name").ToString(), @"(.*)\(");
                    var playerNameB = ScrapeHelper.RegexMappingExpression(propositionPlayerB.SelectToken("$.name").ToString(), @"(.*)\(");
                    var playerA     = ScrapeHelper.FindPlayerInMatch(playerNameA, match);
                    if (playerA == null)
                    {
                        Logger.Warning($"Cannot find any playerA {playerNameA} in match {match.Id}");
                        continue;
                    }

                    var playerB = ScrapeHelper.FindPlayerInMatch(playerNameB, match);
                    if (playerB == null)
                    {
                        Logger.Warning($"Cannot find any playerB {playerNameB} in match {match.Id}");
                        continue;
                    }

                    var priceA   = ScrapeHelper.ConvertMetric(propositionPlayerA.SelectToken("$.returnWin").ToString());
                    var tiePrice = ScrapeHelper.ConvertMetric(propositionTie.SelectToken("$.returnWin").ToString());
                    var priceB   = ScrapeHelper.ConvertMetric(propositionPlayerB.SelectToken("$.returnWin").ToString());

                    Logger.Information($"{playerA.Name} vs {playerB.Name}: {priceA} vs {priceB} | Tie: {tiePrice}");
                    var metric = new PlayerHeadToHead
                    {
                        PlayerAId             = playerA.Id,
                        PlayerBId             = playerB.Id,
                        IsTieIncluded         = tiePrice != null,
                        PlayerAPrice          = priceA,
                        PlayerBPrice          = priceB,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        TiePrice  = tiePrice,
                        MatchId   = match.Id,
                        CreatedAt = DateTime.Now
                    };

                    PlayerHeadToHeads.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            Logger.Information("Scrape metric data complete");
            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }
        protected override async Task ScrapeData()
        {
            const string url    = "https://betbuilder.digitalsportstech.com/api/latestGames?leagueId=123&sb=topsport&status=1";
            var          rawDoc = await ScrapeHelper.GetDocument(url);

            var jDoc = JsonConvert.DeserializeObject <JToken>(rawDoc);

            var rawMatches   = jDoc.SelectTokens("$.data[*]");
            var foundMatches = new List <Match>();

            await UpdateScrapeStatus(10, "Scraping match data");

            Logger.Information("Scraping match data");
            foreach (var rawMatch in rawMatches)
            {
                var sourceMatchId = rawMatch.SelectToken("$.id").ToString();
                if (string.IsNullOrEmpty(sourceMatchId))
                {
                    Logger.Warning("Source Id is null");
                    continue;
                }

                var homeTeam = rawMatch.SelectToken("$.homeTeam.title").ToString();
                var awayTeam = rawMatch.SelectToken("$.visitingTeam.title").ToString();

                var match = ScrapeHelper.FindMatchByHomeAndAwayTeam(TodayMatches, homeTeam, awayTeam);
                if (match == null)
                {
                    Logger.Warning($"Cannot find match with Home team: {homeTeam} and Away team: {awayTeam}");
                    continue;
                }

                match.SourceId = sourceMatchId;
                foundMatches.Add(match);
            }
            Logger.Information("Scrape match data complete");
            await UpdateScrapeStatus(20, "Scrape match data complete");

            await UpdateScrapeStatus(20, "Scraping metric data");

            var rangeProgress = foundMatches.Count != 0 ? 90 / foundMatches.Count : 0;
            var currentRange  = 20;

            Logger.Information("Scraping metric data");
            _rawData = new List <PlayerOverUnder>();
            foreach (var match in foundMatches)
            {
                var metricUrl = $"https://betbuilder.digitalsportstech.com/api/feed?betType=in,18,19&gameId={match.SourceId}&limit=9999&sb=topsport&tz=7";
                rawDoc = await ScrapeHelper.GetDocument(metricUrl);

                jDoc = JsonConvert.DeserializeObject <JToken>(rawDoc);
                var rawMetrics = jDoc.SelectTokens("$.data[*]").ToList();

                currentRange = Math.Min(currentRange + rangeProgress, 80);

                foreach (var rawMetric in rawMetrics)
                {
                    var statistic = rawMetric.SelectToken("$.statistic.title").ToString();
                    var scoreType =
                        statistic.Contains("Pts + Reb + Ast") ? ScoreType.PointReboundAssist :
                        statistic.Contains("Total Rebounds") ? ScoreType.Rebound :
                        statistic.Contains("Assists") ? ScoreType.Assist :
                        statistic.Contains("Points") ? ScoreType.Point :
                        statistic.Contains("Three Point") ? ScoreType.ThreePoint :
                        string.Empty;
                    if (string.IsNullOrEmpty(scoreType))
                    {
                        continue;
                    }

                    var playerName = rawMetric.SelectToken("$.player1.name").ToString();
                    var player     = ScrapeHelper.FindPlayerInMatch(playerName, match);
                    if (player == null)
                    {
                        Logger.Warning($"Cannot find any player {playerName} in match {match.Id}");
                        continue;
                    }

                    var betType = rawMetric.SelectToken("$.betType").ToString();

                    double?over = 0, overLine = 0, under = 0, underLine = 0;
                    switch (betType)
                    {
                    case "18":
                        over     = ScrapeHelper.ConvertMetric(rawMetric.SelectToken("$.markets[0].odds").ToString());
                        overLine = ScrapeHelper.ConvertMetric(rawMetric.SelectToken("$.markets[0].value").ToString());
                        break;

                    case "19":
                        under     = ScrapeHelper.ConvertMetric(rawMetric.SelectToken("$.markets[0].odds").ToString());
                        underLine = ScrapeHelper.ConvertMetric(rawMetric.SelectToken("$.markets[0].value").ToString());
                        break;
                    }

                    var metric = new PlayerOverUnder
                    {
                        MatchId               = match.Id,
                        Over                  = over,
                        OverLine              = overLine,
                        Under                 = under,
                        UnderLine             = underLine,
                        PlayerId              = player.Id,
                        Player                = player,
                        ScoreType             = scoreType,
                        ScrapingInformationId = GetScrapingInformation().Id,
                        CreatedAt             = DateTime.Now
                    };

                    _rawData.Add(metric);

                    var newProgress = GetScrapingInformation().Progress;
                    newProgress = Math.Min(newProgress + currentRange / rawMetrics.Count, currentRange);
                    await UpdateScrapeStatus(newProgress, null);
                }
                await UpdateScrapeStatus(currentRange, null);
            }
            Logger.Information("Scrape metric data complete");

            MergeData();
            Logger.Information("Merge data complete");

            await UpdateScrapeStatus(90, "Scrape metric data complete");
        }