Return different types from web scraper method The 2019 Stack Overflow Developer Survey Results Are InInefficient code inside of getter?Return IEnumerable<KeyValuePair> from a private method; use Dictionary or anon. type?Web scraper for YellWeb-scraper for housing webCounting the number of returning item for a TimeFrameSave to a MySQL database a temporary row with all information about a receiptSemaphoreSlim limit tasksComic Image Web ScraperSending many unique emails at same timeA simple web scraper
Does HR tell a hiring manager about salary negotiations?
Geography at the pixel level
Flight paths in orbit around Ceres?
Star Trek - X-shaped Item on Regula/Orbital Office Starbases
How to type this arrow in math mode?
Getting crown tickets for Statue of Liberty
How to display lines in a file like ls displays files in a directory?
Can there be female White Walkers?
Straighten subgroup lattice
If I can cast sorceries at instant speed, can I use sorcery-speed activated abilities at instant speed?
Is it ok to offer lower paid work as a trial period before negotiating for a full-time job?
How do PCB vias affect signal quality?
Cooking pasta in a water boiler
Does adding complexity mean a more secure cipher?
Why doesn't UInt have a toDouble()?
Is it okay to consider publishing in my first year of PhD?
Is it correct to say the Neural Networks are an alternative way of performing Maximum Likelihood Estimation? if not, why?
How do I free up internal storage if I don't have any apps downloaded?
Why doesn't shell automatically fix "useless use of cat"?
How much of the clove should I use when using big garlic heads?
What is the light source in the black hole images?
"as much details as you can remember"
Why don't hard Brexiteers insist on a hard border to prevent illegal immigration after Brexit?
Did the UK government pay "millions and millions of dollars" to try to snag Julian Assange?
Return different types from web scraper method
The 2019 Stack Overflow Developer Survey Results Are InInefficient code inside of getter?Return IEnumerable<KeyValuePair> from a private method; use Dictionary or anon. type?Web scraper for YellWeb-scraper for housing webCounting the number of returning item for a TimeFrameSave to a MySQL database a temporary row with all information about a receiptSemaphoreSlim limit tasksComic Image Web ScraperSending many unique emails at same timeA simple web scraper
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
$begingroup$
Application Logic
The software take odds data from an internet site and store them inside my own database, this application is basically a scraper.
There are different types of odds, eg:
- FullTime
- Under / Over
- Double Chance
My goal
I want avoid code redundancy, for doing so, I created a method called GetOddsRow which returns a type called GenericOdds, this is the code:
private async Task<List<GenericOdds>> GetOddsRows(Uri fixtureLink, string endpoint, string[] filter)
HtmlDocument doc = new HtmlDocument();
List<GenericOdds> gos = new List<GenericOdds>();
int i = 0;
foreach (string newEndpoint in filter)
//Make a new endpoint to get the odds type.
fixtureLink = new Uri(fixtureLink, endpoint + newEndpoint);
string html = await NetworkHelper.LoadAndWaitForSelector(fixtureLink, _oddsTable);
doc.LoadHtml(html);
//Get only the containers that contains data.
var containers = doc.DocumentNode.SelectNodes("//div[@class='table-container' and not(@style)]");
//The odds are not available for this container.
if (containers.ElementAtOrDefault(i) == null)
break;
GenericOdds go = new GenericOdds();
HtmlNode container = containers[i];
//Get the table which contains the odds.
HtmlNode table = container.SelectSingleNode(".//table[@class='" + _oddsTableClass + "']");
//Get the odds description type.
go.OddsType = container.SelectSingleNode(".//strong[1]//a").InnerText;
//Get all the rows (odds available).
go.Odds = table.SelectNodes(".//tbody//tr[normalize-space()]");
gos.Add(go);
i++;
return gos;
This method have three parameters:
fixtureLink: is the link of the event that contains the odds
endpoint: is the odds type
filter: specify the category of the odds
eg:
[fixtureLink]
https://www.oddsportal.com/soccer/africa/africa-cup-of-nations/libya-south-africa-0QsEg3I8/?r=1#
[endpoint]
over-under;2;
[filter]
0.50;0
The method logic is really simple, I iterate through all the filters available and then join this filter to the endpoint to create a new url, that url will be used to get the odds data.
I store each list of rows (of a specific category) in a new GenericOdds which have the following implementation:
public class GenericOdds
public HtmlNodeCollection Odds get; set;
public string OddsType get; set;
This allow me to organize the odds rows, so I can easily navigate to that list and see the type of the rows, eg:
GenericOdds[0]
HtmlNodeList => 10 elements
OddsType => Over 0.5
GenericOdds[1]
HtmlNodeList => 5 elements
OddsType => Over 1.5
GenericOdds[2]
HtmlNodeList => 3 elements
OddsType => Over 3.5
Now I have 10 methods that call GetOddsRows, for example of Over / Under:
public async Task<List<OverUnder>> GetOverUnder(Uri fixtureLink, OddsCategory cat)
List<OverUnder> overUnders = new List<OverUnder>();
string endpoint = "?r=1#over-under;2;";
if (cat == OddsCategory.Firsthalf)
endpoint = "?r=1#over-under;3";
else if (cat == OddsCategory.Secondhalf)
endpoint = "?r=1#over-under;4";
//Odds filters.
string[] filter = "0.50;0", "1.50;0", "2.50;0", "3.50;0", "4.50;0", "5.50;0", "6.50;0" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
OverUnder overUnder = new OverUnder();
overUnder.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
overUnder.Description = od.OddsType;
//Get event odds
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
overUnder.Over = OddsUtility.ParseOdds(odds);
odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][2]");
overUnder.Under = OddsUtility.ParseOdds(odds);
overUnder.Payout = OddsUtility.GetPayout(tr);
overUnders.Add(overUnder);
);
return overUnders;
as you can see I have an endpoint for the specific type of odds which is Under / Over, this type is defined as below:
public class OverUnder
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Over get; set;
public KeyValuePair<decimal?, int> Under get; set;
public decimal? Payout get; set;
there is also OddsCategory that is an enum that allow me to understand which types of odds need to be downloaded.
as I said before, there are also others odds method eg: GetFullTime, GetDoubleChance, GetHalfTime, etc...
all of these methods vary only by:
- endpoint
- filter
- return type
So my goal is to avoid code redundancy, honestly I don't like the solution of GenericOdds, I'm looking for a way to return different types from GetOddsRow, something like an interface.
Additional methods:
The site is based on ajax request, for handle that I'm using Puppeteer Sharp, and I create the following method:
public static async Task<string> LoadAndWaitForSelector(Uri url, string selector)
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
Headless = true,
ExecutablePath = Environment.GetEnvironmentVariable("CHROME_PATH"),
);
using (Page page = await browser.NewPageAsync())
await page.GoToAsync(url.ToString());
await page.WaitForSelectorAsync(selector);
return await page.GetContentAsync();
this method wait for the selector requested by the caller and return the odds table generated by the ajax request.
Also, if you are looking for another odds type:
public async Task<List<HalfTime>> GetHalfTime(Uri fixtureLink)
List<HalfTime> hfts = new List<HalfTime>();
string endpoint = "?r=1#ht-ft;2;";
//Odds filters.
string[] filter = "27", 30", "33" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
HalfTimeFullTime hf = new HalfTimeFullTime();
hf.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
hf.Description = header.InnerText;
//Get odds.
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
hf.Odds = OddsUtility.ParseOdds(odds);
hfts.Add(hf);
);
return hfts;
and type:
public class HalfTime
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Odds get; set;
c# web-scraping
$endgroup$
add a comment |
$begingroup$
Application Logic
The software take odds data from an internet site and store them inside my own database, this application is basically a scraper.
There are different types of odds, eg:
- FullTime
- Under / Over
- Double Chance
My goal
I want avoid code redundancy, for doing so, I created a method called GetOddsRow which returns a type called GenericOdds, this is the code:
private async Task<List<GenericOdds>> GetOddsRows(Uri fixtureLink, string endpoint, string[] filter)
HtmlDocument doc = new HtmlDocument();
List<GenericOdds> gos = new List<GenericOdds>();
int i = 0;
foreach (string newEndpoint in filter)
//Make a new endpoint to get the odds type.
fixtureLink = new Uri(fixtureLink, endpoint + newEndpoint);
string html = await NetworkHelper.LoadAndWaitForSelector(fixtureLink, _oddsTable);
doc.LoadHtml(html);
//Get only the containers that contains data.
var containers = doc.DocumentNode.SelectNodes("//div[@class='table-container' and not(@style)]");
//The odds are not available for this container.
if (containers.ElementAtOrDefault(i) == null)
break;
GenericOdds go = new GenericOdds();
HtmlNode container = containers[i];
//Get the table which contains the odds.
HtmlNode table = container.SelectSingleNode(".//table[@class='" + _oddsTableClass + "']");
//Get the odds description type.
go.OddsType = container.SelectSingleNode(".//strong[1]//a").InnerText;
//Get all the rows (odds available).
go.Odds = table.SelectNodes(".//tbody//tr[normalize-space()]");
gos.Add(go);
i++;
return gos;
This method have three parameters:
fixtureLink: is the link of the event that contains the odds
endpoint: is the odds type
filter: specify the category of the odds
eg:
[fixtureLink]
https://www.oddsportal.com/soccer/africa/africa-cup-of-nations/libya-south-africa-0QsEg3I8/?r=1#
[endpoint]
over-under;2;
[filter]
0.50;0
The method logic is really simple, I iterate through all the filters available and then join this filter to the endpoint to create a new url, that url will be used to get the odds data.
I store each list of rows (of a specific category) in a new GenericOdds which have the following implementation:
public class GenericOdds
public HtmlNodeCollection Odds get; set;
public string OddsType get; set;
This allow me to organize the odds rows, so I can easily navigate to that list and see the type of the rows, eg:
GenericOdds[0]
HtmlNodeList => 10 elements
OddsType => Over 0.5
GenericOdds[1]
HtmlNodeList => 5 elements
OddsType => Over 1.5
GenericOdds[2]
HtmlNodeList => 3 elements
OddsType => Over 3.5
Now I have 10 methods that call GetOddsRows, for example of Over / Under:
public async Task<List<OverUnder>> GetOverUnder(Uri fixtureLink, OddsCategory cat)
List<OverUnder> overUnders = new List<OverUnder>();
string endpoint = "?r=1#over-under;2;";
if (cat == OddsCategory.Firsthalf)
endpoint = "?r=1#over-under;3";
else if (cat == OddsCategory.Secondhalf)
endpoint = "?r=1#over-under;4";
//Odds filters.
string[] filter = "0.50;0", "1.50;0", "2.50;0", "3.50;0", "4.50;0", "5.50;0", "6.50;0" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
OverUnder overUnder = new OverUnder();
overUnder.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
overUnder.Description = od.OddsType;
//Get event odds
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
overUnder.Over = OddsUtility.ParseOdds(odds);
odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][2]");
overUnder.Under = OddsUtility.ParseOdds(odds);
overUnder.Payout = OddsUtility.GetPayout(tr);
overUnders.Add(overUnder);
);
return overUnders;
as you can see I have an endpoint for the specific type of odds which is Under / Over, this type is defined as below:
public class OverUnder
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Over get; set;
public KeyValuePair<decimal?, int> Under get; set;
public decimal? Payout get; set;
there is also OddsCategory that is an enum that allow me to understand which types of odds need to be downloaded.
as I said before, there are also others odds method eg: GetFullTime, GetDoubleChance, GetHalfTime, etc...
all of these methods vary only by:
- endpoint
- filter
- return type
So my goal is to avoid code redundancy, honestly I don't like the solution of GenericOdds, I'm looking for a way to return different types from GetOddsRow, something like an interface.
Additional methods:
The site is based on ajax request, for handle that I'm using Puppeteer Sharp, and I create the following method:
public static async Task<string> LoadAndWaitForSelector(Uri url, string selector)
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
Headless = true,
ExecutablePath = Environment.GetEnvironmentVariable("CHROME_PATH"),
);
using (Page page = await browser.NewPageAsync())
await page.GoToAsync(url.ToString());
await page.WaitForSelectorAsync(selector);
return await page.GetContentAsync();
this method wait for the selector requested by the caller and return the odds table generated by the ajax request.
Also, if you are looking for another odds type:
public async Task<List<HalfTime>> GetHalfTime(Uri fixtureLink)
List<HalfTime> hfts = new List<HalfTime>();
string endpoint = "?r=1#ht-ft;2;";
//Odds filters.
string[] filter = "27", 30", "33" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
HalfTimeFullTime hf = new HalfTimeFullTime();
hf.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
hf.Description = header.InnerText;
//Get odds.
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
hf.Odds = OddsUtility.ParseOdds(odds);
hfts.Add(hf);
);
return hfts;
and type:
public class HalfTime
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Odds get; set;
c# web-scraping
$endgroup$
add a comment |
$begingroup$
Application Logic
The software take odds data from an internet site and store them inside my own database, this application is basically a scraper.
There are different types of odds, eg:
- FullTime
- Under / Over
- Double Chance
My goal
I want avoid code redundancy, for doing so, I created a method called GetOddsRow which returns a type called GenericOdds, this is the code:
private async Task<List<GenericOdds>> GetOddsRows(Uri fixtureLink, string endpoint, string[] filter)
HtmlDocument doc = new HtmlDocument();
List<GenericOdds> gos = new List<GenericOdds>();
int i = 0;
foreach (string newEndpoint in filter)
//Make a new endpoint to get the odds type.
fixtureLink = new Uri(fixtureLink, endpoint + newEndpoint);
string html = await NetworkHelper.LoadAndWaitForSelector(fixtureLink, _oddsTable);
doc.LoadHtml(html);
//Get only the containers that contains data.
var containers = doc.DocumentNode.SelectNodes("//div[@class='table-container' and not(@style)]");
//The odds are not available for this container.
if (containers.ElementAtOrDefault(i) == null)
break;
GenericOdds go = new GenericOdds();
HtmlNode container = containers[i];
//Get the table which contains the odds.
HtmlNode table = container.SelectSingleNode(".//table[@class='" + _oddsTableClass + "']");
//Get the odds description type.
go.OddsType = container.SelectSingleNode(".//strong[1]//a").InnerText;
//Get all the rows (odds available).
go.Odds = table.SelectNodes(".//tbody//tr[normalize-space()]");
gos.Add(go);
i++;
return gos;
This method have three parameters:
fixtureLink: is the link of the event that contains the odds
endpoint: is the odds type
filter: specify the category of the odds
eg:
[fixtureLink]
https://www.oddsportal.com/soccer/africa/africa-cup-of-nations/libya-south-africa-0QsEg3I8/?r=1#
[endpoint]
over-under;2;
[filter]
0.50;0
The method logic is really simple, I iterate through all the filters available and then join this filter to the endpoint to create a new url, that url will be used to get the odds data.
I store each list of rows (of a specific category) in a new GenericOdds which have the following implementation:
public class GenericOdds
public HtmlNodeCollection Odds get; set;
public string OddsType get; set;
This allow me to organize the odds rows, so I can easily navigate to that list and see the type of the rows, eg:
GenericOdds[0]
HtmlNodeList => 10 elements
OddsType => Over 0.5
GenericOdds[1]
HtmlNodeList => 5 elements
OddsType => Over 1.5
GenericOdds[2]
HtmlNodeList => 3 elements
OddsType => Over 3.5
Now I have 10 methods that call GetOddsRows, for example of Over / Under:
public async Task<List<OverUnder>> GetOverUnder(Uri fixtureLink, OddsCategory cat)
List<OverUnder> overUnders = new List<OverUnder>();
string endpoint = "?r=1#over-under;2;";
if (cat == OddsCategory.Firsthalf)
endpoint = "?r=1#over-under;3";
else if (cat == OddsCategory.Secondhalf)
endpoint = "?r=1#over-under;4";
//Odds filters.
string[] filter = "0.50;0", "1.50;0", "2.50;0", "3.50;0", "4.50;0", "5.50;0", "6.50;0" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
OverUnder overUnder = new OverUnder();
overUnder.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
overUnder.Description = od.OddsType;
//Get event odds
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
overUnder.Over = OddsUtility.ParseOdds(odds);
odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][2]");
overUnder.Under = OddsUtility.ParseOdds(odds);
overUnder.Payout = OddsUtility.GetPayout(tr);
overUnders.Add(overUnder);
);
return overUnders;
as you can see I have an endpoint for the specific type of odds which is Under / Over, this type is defined as below:
public class OverUnder
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Over get; set;
public KeyValuePair<decimal?, int> Under get; set;
public decimal? Payout get; set;
there is also OddsCategory that is an enum that allow me to understand which types of odds need to be downloaded.
as I said before, there are also others odds method eg: GetFullTime, GetDoubleChance, GetHalfTime, etc...
all of these methods vary only by:
- endpoint
- filter
- return type
So my goal is to avoid code redundancy, honestly I don't like the solution of GenericOdds, I'm looking for a way to return different types from GetOddsRow, something like an interface.
Additional methods:
The site is based on ajax request, for handle that I'm using Puppeteer Sharp, and I create the following method:
public static async Task<string> LoadAndWaitForSelector(Uri url, string selector)
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
Headless = true,
ExecutablePath = Environment.GetEnvironmentVariable("CHROME_PATH"),
);
using (Page page = await browser.NewPageAsync())
await page.GoToAsync(url.ToString());
await page.WaitForSelectorAsync(selector);
return await page.GetContentAsync();
this method wait for the selector requested by the caller and return the odds table generated by the ajax request.
Also, if you are looking for another odds type:
public async Task<List<HalfTime>> GetHalfTime(Uri fixtureLink)
List<HalfTime> hfts = new List<HalfTime>();
string endpoint = "?r=1#ht-ft;2;";
//Odds filters.
string[] filter = "27", 30", "33" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
HalfTimeFullTime hf = new HalfTimeFullTime();
hf.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
hf.Description = header.InnerText;
//Get odds.
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
hf.Odds = OddsUtility.ParseOdds(odds);
hfts.Add(hf);
);
return hfts;
and type:
public class HalfTime
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Odds get; set;
c# web-scraping
$endgroup$
Application Logic
The software take odds data from an internet site and store them inside my own database, this application is basically a scraper.
There are different types of odds, eg:
- FullTime
- Under / Over
- Double Chance
My goal
I want avoid code redundancy, for doing so, I created a method called GetOddsRow which returns a type called GenericOdds, this is the code:
private async Task<List<GenericOdds>> GetOddsRows(Uri fixtureLink, string endpoint, string[] filter)
HtmlDocument doc = new HtmlDocument();
List<GenericOdds> gos = new List<GenericOdds>();
int i = 0;
foreach (string newEndpoint in filter)
//Make a new endpoint to get the odds type.
fixtureLink = new Uri(fixtureLink, endpoint + newEndpoint);
string html = await NetworkHelper.LoadAndWaitForSelector(fixtureLink, _oddsTable);
doc.LoadHtml(html);
//Get only the containers that contains data.
var containers = doc.DocumentNode.SelectNodes("//div[@class='table-container' and not(@style)]");
//The odds are not available for this container.
if (containers.ElementAtOrDefault(i) == null)
break;
GenericOdds go = new GenericOdds();
HtmlNode container = containers[i];
//Get the table which contains the odds.
HtmlNode table = container.SelectSingleNode(".//table[@class='" + _oddsTableClass + "']");
//Get the odds description type.
go.OddsType = container.SelectSingleNode(".//strong[1]//a").InnerText;
//Get all the rows (odds available).
go.Odds = table.SelectNodes(".//tbody//tr[normalize-space()]");
gos.Add(go);
i++;
return gos;
This method have three parameters:
fixtureLink: is the link of the event that contains the odds
endpoint: is the odds type
filter: specify the category of the odds
eg:
[fixtureLink]
https://www.oddsportal.com/soccer/africa/africa-cup-of-nations/libya-south-africa-0QsEg3I8/?r=1#
[endpoint]
over-under;2;
[filter]
0.50;0
The method logic is really simple, I iterate through all the filters available and then join this filter to the endpoint to create a new url, that url will be used to get the odds data.
I store each list of rows (of a specific category) in a new GenericOdds which have the following implementation:
public class GenericOdds
public HtmlNodeCollection Odds get; set;
public string OddsType get; set;
This allow me to organize the odds rows, so I can easily navigate to that list and see the type of the rows, eg:
GenericOdds[0]
HtmlNodeList => 10 elements
OddsType => Over 0.5
GenericOdds[1]
HtmlNodeList => 5 elements
OddsType => Over 1.5
GenericOdds[2]
HtmlNodeList => 3 elements
OddsType => Over 3.5
Now I have 10 methods that call GetOddsRows, for example of Over / Under:
public async Task<List<OverUnder>> GetOverUnder(Uri fixtureLink, OddsCategory cat)
List<OverUnder> overUnders = new List<OverUnder>();
string endpoint = "?r=1#over-under;2;";
if (cat == OddsCategory.Firsthalf)
endpoint = "?r=1#over-under;3";
else if (cat == OddsCategory.Secondhalf)
endpoint = "?r=1#over-under;4";
//Odds filters.
string[] filter = "0.50;0", "1.50;0", "2.50;0", "3.50;0", "4.50;0", "5.50;0", "6.50;0" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
OverUnder overUnder = new OverUnder();
overUnder.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
overUnder.Description = od.OddsType;
//Get event odds
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
overUnder.Over = OddsUtility.ParseOdds(odds);
odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][2]");
overUnder.Under = OddsUtility.ParseOdds(odds);
overUnder.Payout = OddsUtility.GetPayout(tr);
overUnders.Add(overUnder);
);
return overUnders;
as you can see I have an endpoint for the specific type of odds which is Under / Over, this type is defined as below:
public class OverUnder
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Over get; set;
public KeyValuePair<decimal?, int> Under get; set;
public decimal? Payout get; set;
there is also OddsCategory that is an enum that allow me to understand which types of odds need to be downloaded.
as I said before, there are also others odds method eg: GetFullTime, GetDoubleChance, GetHalfTime, etc...
all of these methods vary only by:
- endpoint
- filter
- return type
So my goal is to avoid code redundancy, honestly I don't like the solution of GenericOdds, I'm looking for a way to return different types from GetOddsRow, something like an interface.
Additional methods:
The site is based on ajax request, for handle that I'm using Puppeteer Sharp, and I create the following method:
public static async Task<string> LoadAndWaitForSelector(Uri url, string selector)
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
Headless = true,
ExecutablePath = Environment.GetEnvironmentVariable("CHROME_PATH"),
);
using (Page page = await browser.NewPageAsync())
await page.GoToAsync(url.ToString());
await page.WaitForSelectorAsync(selector);
return await page.GetContentAsync();
this method wait for the selector requested by the caller and return the odds table generated by the ajax request.
Also, if you are looking for another odds type:
public async Task<List<HalfTime>> GetHalfTime(Uri fixtureLink)
List<HalfTime> hfts = new List<HalfTime>();
string endpoint = "?r=1#ht-ft;2;";
//Odds filters.
string[] filter = "27", 30", "33" ;
List<GenericOdds> gos = await GetOddsRows(fixtureLink, endpoint, filter);
gos.ForEach(od =>
foreach(HtmlNode tr in od.Odds)
HalfTimeFullTime hf = new HalfTimeFullTime();
hf.Book = tr.SelectSingleNode(".//td//div//a[2]").InnerText;
hf.Description = header.InnerText;
//Get odds.
HtmlNode odds = tr.SelectSingleNode(".//td[contains(@class, 'odds')][1]");
hf.Odds = OddsUtility.ParseOdds(odds);
hfts.Add(hf);
);
return hfts;
and type:
public class HalfTime
public string Book get; set;
public string Description get; set;
public KeyValuePair<decimal?, int> Odds get; set;
c# web-scraping
c# web-scraping
edited 3 hours ago
sfarzoso
asked 4 hours ago
sfarzososfarzoso
1334
1334
add a comment |
add a comment |
0
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217276%2freturn-different-types-from-web-scraper-method%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
0
active
oldest
votes
0
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f217276%2freturn-different-types-from-web-scraper-method%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown