¿Cómo hacer llamadas asíncronas usando HtmlAgilityPack?

c# html-agility-pack

Pregunta

Estoy tratando de obtener la tabla con table-matches identificación disponibles aquí . El problema es que la tabla se carga utilizando ajax por lo que no obtengo el código html completo cuando descargo la página:

string url = "http://www.oddsportal.com/matches/soccer/20180701/";

using (HttpClient client = new HttpClient())
{
    using (HttpResponseMessage response = client.GetAsync(url).Result)
    {
        using (HttpContent content = response.Content)
        {
            string result = content.ReadAsStringAsync().Result;
        }
    }
}

el html devuelto no contiene ninguna tabla, por lo que traté de ver si hay un problema de la biblioteca, de hecho establecí en Chrome (específicamente en la consola Dev Dev F12), el mismo resultado en el navegador.

Fox solucionó este problema, pero pensé en usar un WebBrowser , en particular:

webBrowser.Navigate("oddsportal.com/matches/soccer/20140221/"); 
HtmlElementCollection elements = webBrowser.Document.GetElementsByTagName("table");

pero quiero preguntar si puedo cargar también el html completo haciendo llamadas asíncronas, ¿alguien ha encontrado un problema similar?

¿Podría por favor compartir una solución? Gracias.

Respuesta aceptada

El principal problema con esta página es que el contenido dentro table-matches se carga a través de ajax. Y ni HttpClient ni HtmlAgilityPack no pueden esperar a que se ejecute ajax. Por lo tanto, necesita un enfoque diferente.

Enfoque # 1 : use cualquier navegador sin cabeza como PuppeteerSharp

using PuppeteerSharp;
using System;
using System.Threading.Tasks;

namespace PuppeteerSharpDemo
{
    class Program
    {
        private static String url = "http://www.oddsportal.com/matches/soccer/20180701/";

        static void Main(string[] args)
        {
            var htmlAsTask = LoadAndWaitForSelector(url, "#table-matches .table-main");
            htmlAsTask.Wait();
            Console.WriteLine(htmlAsTask.Result);

            Console.ReadKey();
        }

        public static async Task<string> LoadAndWaitForSelector(String url, String selector)
        {
            var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                ExecutablePath = @"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
            });
            using (Page page = await browser.NewPageAsync())
            {
                await page.GoToAsync(url);
                await page.WaitForSelectorAsync(selector);
                return await page.GetContentAsync();
            }
        }
    }
}

Con el propósito de la limpieza, he publicado la salida aquí . Y una vez que obtenga contenido html, podrá analizarlo con HtmlAgilityPack .

Enfoque # 2 - Use el Selenium puro WebDriver

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;

namespace SeleniumDemo
{
    class Program
    {
        private static IWebDriver webDriver;
        private static TimeSpan defaultWait = TimeSpan.FromSeconds(10);
        private static String targetUrl = "http://www.oddsportal.com/matches/soccer/20180701/";
        private static String driversDir = @"../../Drivers/";

        static void Main(string[] args)
        {
            webDriver = new ChromeDriver(driversDir);
            webDriver.Navigate().GoToUrl(targetUrl);
            IWebElement table = webDriver.FindElement(By.Id("table-matches"));
            var innerHtml = table.GetAttribute("innerHTML");
        }

        #region (!) I didn't even use this, but it can be useful (!)
        public static IWebElement FindElement(By by)
        {
            try
            {
                WaitForAjax();
                var wait = new WebDriverWait(webDriver, defaultWait);
                return wait.Until(driver => driver.FindElement(by));
            }
            catch
            {
                return null;
            }
        }

        public static void WaitForAjax()
        {
            var wait = new WebDriverWait(webDriver, defaultWait);
            wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
        }
        #endregion
    }
}

Enfoque # 3 - Simular solicitudes ajax

Si analiza la carga de la página utilizando Fiddler o el generador de perfiles del navegador (F12), puede ver que todos los datos vienen con estas dos solicitudes:

violinista solicita raspado deportivo Así que puedes intentar ejecutarlos directamente usando HttpClient . Pero en este caso, es posible que deba hacer un seguimiento de los encabezados de autorización y quizás algo más con cada solicitud HTTP.



Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué