Html Agility Pack no puede encontrar el elemento utilizando xpath pero funciona bien con WebDriver

.net html-agility-pack webdriver xpath

Pregunta

Ya he visto estas preguntas 1 y 2 pero no funciona para mí.

Estoy creando el Xpath para objetos que funciona bien desde WebDriver, pero al intentar seleccionar un nodo mediante HtmlAgilityPack no funciona en algunos casos.

Estoy usando el último HtmlAgilityPack 1.4.9

Por ejemplo, aquí hay una página.

introduzca la descripción de la imagen aquí

La xpath para el objeto resaltado en rojo es

// sección [@ id = 'main-content'] / div 2 / div / div / div / div / div / p 1 / a

Similarmente otro objeto como se muestra en la imagen.

introduzca la descripción de la imagen aquí

Es xpath es

// sección [@ id = 'main-content'] / div 2 / div / div / div / div / div / ul / li 2 / a

Ambos Xpath funcionan perfectamente bien desde WebDriver pero no pueden encontrar ningún objeto del paquete HtmlAgility.

Por el primero que probé

HtmlAgilityPack.HtmlNode.ElementsFlags.Remove ("p")

Comenzó a funcionar pero ¿por qué se requiere? Tampoco hay suerte para el segundo.

¿Hay alguna lista de etiquetas específicas que se necesiten eliminar de ElementFlags? Si hubiera alguno, ¿cuál sería su impacto?

Mi requisito es recuperar objetos usando Xpath de HtmlAgility pack tal como funciona WebDriver.

Cualquier ayuda será apreciada.

EDITAR 1:

El XPATH que recibimos de HAP también son largos como div / div / div / div / div / div Aquí está el código VB.Net para el ejemplo dado por Sir Simon

Dim selectedNode As HtmlAgilityPack.HtmlNode = htmlAgilityDoc.DocumentNode.SelectSingleNode("//section[@id='main-content']//div[@class='pane-content']//a")

Dim xpathValue As String = selectedNode.XPath

Entonces el xpathValue que obtenemos de HAP es

/ html 1 / body 1 / sección 1 / div 2 / div 1 / div 1 / div 1 / div 1 / div 1 / a 1

Respuesta aceptada

WebDriver siempre se basará en el navegador de destino cuando trabaje con XPATH. Técnicamente, es solo un puente elegante al navegador (ya sea que el navegador sea Firefox o Chrome - IE hasta 11 no es compatible con XPATH)

Desafortunadamente, el DOM (estructura de elementos y atributos) que reside en la memoria del navegador no es el mismo que el DOM que probablemente proporcionó al paquete de agilidad de HTML. Podría ser lo mismo si cargó el HAP con el contenido del DOM desde la memoria del navegador (un equivalente a document.OuterHtml por ejemplo). En general, este no es el caso porque los desarrolladores utilizan HAP para eliminar sitios sin un navegador, por lo que lo alimentan desde un flujo de red (desde una solicitud HTTP GET) o desde un archivo sin formato.

Este problema es fácil de demostrar. Por ejemplo, si creas un archivo que contiene solo esto:

<table><tr><td>hello world</td></tr></table>

(No html, ni etiqueta de cuerpo, esto es, de hecho, un archivo html no válido)

Con HAP puedes cargarlo así:

HtmlDocument doc = new HtmlDocument();
doc.Load(myFile);

Y la estructura que HAP creará es simplemente esto:

+table
 +tr
  +td
   'hello world'

El HAP no es un navegador, es un analizador y realmente no conoce las especificaciones HTML, solo sabe cómo analizar un montón de etiquetas y construir un DOM con él. No sabe, por ejemplo, que un documento debe comenzar con HTML y debe contener un BODY, o que un elemento TABLE siempre tiene un hijo TBODY cuando es inferido por un navegador.

Sin embargo, en un navegador Chrome, si abres este archivo, lo inspeccionas y le pides a XPATH el elemento TD, informará esto:

/html/body/table/tbody/tr/td

Porque Chrome acaba de hacer esto por sí mismo ... Como ves, los dos sistemas no coinciden.

Tenga en cuenta que si tiene atributos de id disponibles en el código fuente HTML, la historia es mejor, por ejemplo, con el siguiente código HTML:

<table><tr><td id='hw'>hello world</td></tr></table>

Chrome reportará el siguiente XPATH (intentará usar los atributos de id tanto como sea posible):

//*[@id="hw"]

Que se puede utilizar en HAP también. Pero, esto no funciona todo el tiempo sin embargo. Por ejemplo, con el siguiente HTML

<table id='hw'><tr><td>hello world</td></tr></table>

Chrome ahora producirá este XPATH al TD:

//*[@id="mytable"]/tbody/tr/td

Como ve, esto no se puede usar en HAP nuevamente debido a ese TBODY inferido.

Entonces, al final, no puedes usar ciegamente XPATH generado por los navegadores en otros contextos que en esos navegadores. En otros contextos, deberás encontrar otros discriminantes.

En realidad, personalmente creo que es algo bueno porque hará que tu XPATH sea más resistente a los cambios. Pero tendrás que pensar :-)

Ahora volvamos a su caso :)

El siguiente caso de consola de ejemplo de C # debería funcionar bien:

  static void Main(string[] args)
  {
      var web = new HtmlWeb();
      var doc = web.Load("http://www2.epa.gov/languages/traditional-chinese");
      var node = doc.DocumentNode.SelectSingleNode("//section[@id='main-content']//div[@class='pane-content']//a");
      Console.WriteLine(node.OuterHtml); // displays <a href="http://www.oehha.ca.gov/fish/pdf/59329_CHINESE.pdf">...etc...</a>"
  }

Si observa la estructura de la secuencia o el archivo (o incluso lo que muestra el navegador, pero tenga cuidado, evite los TBODY ...), lo más fácil es

  • encontrar una id (al igual que el navegador) y / o
  • encuentre elementos o atributos únicos de hijos o nietos debajo de esto, recursivamente o no
  • Evitar XPATHs demasiado precisos. Cosas como p/p/p/div/a/div/whatever sea ​​malo

Entonces, aquí, después del atributo id main-content , solo buscamos (recursivamente con // ) un DIV que tiene una clase especial y buscamos (nuevamente recursivamente) el primer hijo A disponible.

Este XPATH debería funcionar en webdriver y en HAP.

Tenga en cuenta que XPATH también funciona: //div[@class='pane-content']//a pero a mi me parece un poco flojo. Poner el pie en los atributos de id suele ser una buena idea.



Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow