Html Agility Pack - lectura div InnerText en tabla

c# html-agility-pack web-scraping

Pregunta

Mi problema es que no puedo obtener div InnerText de la tabla. He extraído con éxito diferentes tipos de datos, pero no sé cómo leer div de la tabla.

En la siguiente imagen, he resaltado div, y necesito obtener InnerText de ella, en este caso, el número 3.

Haga clic aquí para ver la primera imagen

Estoy tratando de lograr esto usando la siguiente ruta:

"//div[@class='kal']//table//tr[2]/td[1]/div[@class='cipars']"

Pero estoy obteniendo el siguiente error:

Haga clic aquí para ver la imagen del mensaje de error

Suponiendo que el resto del código esté escrito correctamente, ¿podría alguien dirigirme en la dirección correcta? He estado tratando de resolver esto, pero no puedo obtener ningún resultado.

Respuesta aceptada

Entonces, su problema es que confía en posiciones dentro de su XPath. Si bien esto puede estar bien en algunos casos, no está aquí, porque está esperando que el primer td en un tr dado tenga un div con la clase.

Mirando la fuente en Chrome, muestra que no siempre es así. Puede ver esto comparando el elemento "1" en el calendario, a "2" y "3". Notará que el elemento "1" tiene una serie de elementos a su alrededor, que los demás no.

Su consulta XPath original no devuelve un elemento, es por eso que está recibiendo el error. En el caso de que la consulta XPath que proporcione a HtmlAgilityPack no produzca un elemento DOM, devolverá un valor nulo.

Ahora, debido a que no ha mostrado su código completo, no sé cómo se está ejecutando este código. Sin embargo, supongo que está intentando recorrer todos los elementos del calendario. En cualquier caso, tiene varias formas de hacer esto, pero le mostraré que con el selector de XPath descendant , puede agarrar todo el lote de una vez:

//div[@class='kal']//table//descendant::div[@class='cipars']

Esto devolverá todos los elementos del calendario (es decir, de 1 a 30).

Sin embargo, para obtener todos los elementos en una fila en particular, simplemente puede pegar ese tr en la consulta:

//div[@class='kal']//table//tr[3]/descendant::div[@class='cipars']

Esto devolvería 2 a 8 (la segunda fila de elementos del calendario).

Para apuntar a uno específico, bueno, tendrá que hacer una suposición sobre el código fuente del sitio web. Parece que cada div "cipars" tiene un ancestro de td con datums clase ... así que para obtener el valor "3" de tu pregunta:

//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']

Esperemos que esto sea suficiente para mostrar al menos el problema.

Editar

Aunque tiene un problema de XPath, también tiene otro problema.

El sitio está creado muy extrañamente. El calendario se carga de una manera extraña. Cuando llego a esa URL, el calendario se crea mediante algún Javascript que llama a un servicio web XML (escrito en PHP) que luego calcula la table completa que se usará para el calendario.

Debido a que esto es Javascript (código del lado del cliente), HtmlAgilityPack no lo ejecutará. Por lo tanto, HtmlAgilityPack ni siquiera "ve" la tabla. Por lo tanto, las consultas en su contra vuelven como "no encontradas" (null).

Formas de evitar esto: 1) Use una herramienta que llame a los scripts. Con esto, me refiero a cargar un navegador. Una gran herramienta para usar para esto se llama Selenium . Esta será probablemente la mejor solución general porque significa que se llamará a todos los scripts utilizados por el sitio. Aún puede usar XPath con él, por lo que sus consultas no cambiarán.

La segunda forma es enviar una solicitud al mismo servicio web que la página. Esto es básicamente para obtener de vuelta el mismo HTML que la página está recibiendo, y el uso que con HtmlAgilityPack. ¿Como hacemos eso?

Bueno, usted puede POST fácilmente datos a un servicio web usando C #. Solo por facilidad de uso, he robado el código de esta pregunta SO . Con esto, podemos enviar la misma solicitud que la página y recuperar el mismo HTML.

Entonces, para enviar algunos datos POST, generamos un método así ...

public static string SendPost(string url, string postData)
{
    string webpageContent = string.Empty;

    byte[] byteArray = Encoding.UTF8.GetBytes(postData);

    HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
    webRequest.Method = "POST";
    webRequest.ContentType = "application/x-www-form-urlencoded";
    webRequest.ContentLength = byteArray.Length;

    using (Stream webpageStream = webRequest.GetRequestStream())
    {
        webpageStream.Write(byteArray, 0, byteArray.Length);
    }

    using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
    {
        using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
        {
            webpageContent = reader.ReadToEnd();
        }
    }

    return webpageContent;
}

Podemos llamarlo así:

string responseBody = SendPost("http://lekcijas.va.lv/lekcijas_request.php", "nodala=IT&kurss=1&gads=2013&menesis=9&c_dala=");

¿Cómo conseguí esto? Bueno, el archivo php que estamos llamando es el servicio web que es la página, y los datos POST también lo son. La forma en que descubrí qué datos envía al servicio es mediante la depuración del Javascript (usando la Consola de desarrollador de Chrome), pero puede notar que es casi lo mismo que está en la URL. Eso parece ser intencional.

El responseBody que se devuelve es el HTML físico de solo la table para el calendario.

¿Qué hacemos con eso ahora? Lo cargamos en HtmlAgilityPack, porque puede aceptar HTML puro.

var document = new HtmlDocument();
document.LoadHtml(webpageContent);

Ahora, pegamos ese XPath original en:

var node = document.DocumentNode.SelectSingleNode("//div[@class='kal']//table//tr[3]//td[@class='datums'][2]/div[@class='cipars']");

Ahora, imprimimos lo que esperamos sea "3":

Console.WriteLine(node.InnerText);

Mi salida, ejecutándolo localmente, es de hecho: 3 .

Sin embargo, aunque esto le ayude a superar el problema que está teniendo, asumo que el resto del sitio es así. Si este es el caso, es posible que aún puedas solucionarlo utilizando la técnica anterior, pero las herramientas como Selenium se crearon por esta misma razón.



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é