Html Agility Pack ne peut pas trouver un élément utilisant xpath mais il fonctionne correctement avec WebDriver.

.net html-agility-pack webdriver xpath

Question

J'ai déjà vu ces questions 1 et 2 mais ne fonctionne pas pour moi.

Je crée le chemin Xpath pour les objets qui fonctionne correctement avec WebDriver, mais lorsque vous essayez de sélectionner un noeud à l'aide de HtmlAgilityPack, il ne fonctionne pas dans certains cas.

J'utilise la dernière version de HtmlAgilityPack 1.4.9

Par exemple, voici une page.

entrez la description de l'image ici

Le xpath pour l’objet surligné en rouge est

// section [@ id = 'contenu principal'] / div 2 / div / div / div / div / div / p 1 / a

De même un autre objet comme indiqué dans l'image

entrez la description de l'image ici

C'est xpath c'est

// section [@ id = 'contenu principal'] / div 2 / div / div / div / div / div / ul / li 2 / a

Ces deux Xpath fonctionnent parfaitement avec WebDriver mais ne peuvent trouver aucun objet dans le pack HtmlAgility.

Pour le premier j'ai essayé

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

Cela a commencé à fonctionner mais pourquoi est-ce nécessaire? De plus, il n'y a pas de chance pour le second.

Existe-t-il une liste de balises spécifiques à supprimer de ElementFlags? S'il y en avait, quel serait son impact?

Mon exigence est de récupérer des objets en utilisant Xpath à partir du pack HtmlAgility, exactement comme WebDriver.

Toute aide est la bienvenue.

EDIT 1:

Les XPATH que nous obtenons de HAP sont également longs, comme div / div / div / div / div / a. Voici le code VB.Net pour l'exemple donné par 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

Alors la valeur xpathValue que nous recevons de HAP est

/ html 1 / body 1 / section 1 / div 2 / div 1 / div 1 / div 1 / div 1 / div 1 / a 1

Réponse acceptée

WebDriver utilisera toujours le navigateur cible lors de l'utilisation de XPATH. Techniquement, il ne s'agit que d'un simple pont vers le navigateur (qu'il s'agisse de Firefox ou de Chrome - IE 11 ne prend pas en charge XPATH)

Malheureusement, les DOM (éléments et structure d'attributs) qui résident dans la mémoire du navigateur ne sont pas les mêmes que ceux que vous avez probablement fournis au pack d'agilité HTML. Il pourrait en être de même si vous avez chargé le HAP avec le contenu du DOM depuis la mémoire du navigateur (équivalent à document.OuterHtml par exemple). En général, ce n'est pas le cas, car les développeurs utilisent HAP pour supprimer des sites sans navigateur. Ils l'alimentent donc à partir d'un flux réseau (à partir d'une requête HTTP GET) ou d'un fichier brut.

Ce problème est facile à démontrer. Par exemple, si vous créez un fichier contenant uniquement ceci:

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

(pas de code HTML, pas de balise body, il s’agit en fait d’un fichier HTML non valide)

Avec HAP, vous pouvez le charger comme ceci:

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

Et la structure proposée par HAP est simplement la suivante:

+table
 +tr
  +td
   'hello world'

HAP n'est pas un navigateur, c'est un analyseur et il ne connaît pas vraiment les spécifications HTML, il sait juste comment analyser un groupe de balises et construire un DOM avec celui-ci. Il ne sait pas par exemple qu'un document doit commencer par HTML, et doit contenir un corps, ou qu'un élément table a toujours un enfant TBODY lorsqu'il est déduit par un navigateur.

Dans un navigateur Chrome, si vous ouvrez ce fichier, inspectez-le et demandez à l'élément XPATH l'élément TD, il signalera ceci:

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

Parce que Chrome vient de le créer par lui-même ... Comme vous le voyez, les deux systèmes ne correspondent pas.

Notez que si vous avez des attributs id disponibles dans le code HTML source, le récit est meilleur, par exemple, avec le code HTML suivant:

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

Chrome signalera le XPATH suivant (il essaiera d'utiliser autant que possible les attributs id ):

//*[@id="hw"]

Ce qui peut également être utilisé dans HAP. Mais, cela ne fonctionne pas tout le temps cependant. Par exemple, avec le code HTML suivant

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

Chrome va maintenant produire cet XPATH au TD:

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

comme vous le voyez, cela ne peut plus être utilisé dans HAP à cause de cette hypothèse TBODY.

Donc, au final, vous ne pouvez pas utiliser aveuglément XPATH généré par les navigateurs dans d'autres contextes que dans ces navigateurs. Dans d'autres contextes, vous devrez trouver d'autres discriminants.

En fait, je pense personnellement que c'est une bonne chose, car cela rendra votre XPATH plus résistant aux changements. Mais vous devrez réfléchir :-)

Revenons maintenant à ton cas :)

Le cas suivant de la console exemple C # devrait fonctionner correctement:

  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 vous regardez la structure du flux ou du fichier (ou même ce que le navigateur affiche, mais faites attention, évitez les TBODY ...), le plus simple est de

  • trouver un id (comme le navigateur le fait) et / ou
  • trouver des éléments ou attributs uniques d'enfants ou de grands enfants au-dessous, récursivement ou non
  • évitez les XPATH trop précis. Des choses comme p/p/p/div/a/div/whatever est mauvais

Donc, ici, après l'attribut main-content id , nous cherchons simplement (de manière récursive avec // ) un DIV ayant une classe spéciale et nous cherchons (encore une fois récursivement) le premier enfant A disponible.

Cet XPATH devrait fonctionner dans WebDriver et dans HAP.

Notez que cette XPATH fonctionne également: //div[@class='pane-content']//a mais elle me semble un peu lâche. Mettre le pied sur les attributs id est souvent une bonne idée.



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow