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;








1












$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:



  1. FullTime

  2. Under / Over

  3. 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:




  1. fixtureLink: is the link of the event that contains the odds


  2. endpoint: is the odds type


  3. 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:



  1. endpoint

  2. filter

  3. 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;










share|improve this question











$endgroup$


















    1












    $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:



    1. FullTime

    2. Under / Over

    3. 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:




    1. fixtureLink: is the link of the event that contains the odds


    2. endpoint: is the odds type


    3. 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:



    1. endpoint

    2. filter

    3. 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;










    share|improve this question











    $endgroup$














      1












      1








      1





      $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:



      1. FullTime

      2. Under / Over

      3. 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:




      1. fixtureLink: is the link of the event that contains the odds


      2. endpoint: is the odds type


      3. 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:



      1. endpoint

      2. filter

      3. 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;










      share|improve this question











      $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:



      1. FullTime

      2. Under / Over

      3. 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:




      1. fixtureLink: is the link of the event that contains the odds


      2. endpoint: is the odds type


      3. 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:



      1. endpoint

      2. filter

      3. 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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 3 hours ago







      sfarzoso

















      asked 4 hours ago









      sfarzososfarzoso

      1334




      1334




















          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
          );



          );













          draft saved

          draft discarded


















          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















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          名間水力發電廠 目录 沿革 設施 鄰近設施 註釋 外部連結 导航菜单23°50′10″N 120°42′41″E / 23.83611°N 120.71139°E / 23.83611; 120.7113923°50′10″N 120°42′41″E / 23.83611°N 120.71139°E / 23.83611; 120.71139計畫概要原始内容臺灣第一座BOT 模式開發的水力發電廠-名間水力電廠名間水力發電廠 水利署首件BOT案原始内容《小檔案》名間電廠 首座BOT水力發電廠原始内容名間電廠BOT - 經濟部水利署中區水資源局

          格濟夫卡 參考資料 导航菜单51°3′40″N 34°2′21″E / 51.06111°N 34.03917°E / 51.06111; 34.03917ГезівкаПогода в селі 编辑或修订