Html Agility Pack无法使用xpath找到元素,但它可以与WebDriver一起使用

时间:2015-06-16 07:21:58

标签: .net xpath webdriver html-agility-pack

我已经看到了这些问题12但不适合我。

我正在为WebDriver工作正常的对象创建Xpath,但是当尝试使用HtmlAgilityPack选择节点时,它在某些情况下无效。

我正在使用最新的HtmlAgilityPack 1.4.9

例如,Here是一个页面。

enter image description here

以红色突出显示的对象的xpath是

  

//部[@id ='主内容'] / DIV 2 / DIV / DIV / DIV / DIV / DIV / P 1 / A

同样是另一个对象,如图所示

enter image description here

它的xpath是

  

//部[@id =&#39;主内容&#39;] / DIV 2 / DIV / DIV / DIV / DIV / DIV / UL /利2 / A < / p>

这些Xpath在WebDriver中工作得非常好,但无法从HtmlAgility包中找到任何对象。

对于我试过的第一个

  

HtmlAgilityPack.HtmlNode.ElementsFlags.Remove(&#34; P&#34)

它开始起作用,但为什么需要它? 第二个也没有运气。

是否需要从ElementFlags中删除任何特定标记列表?如果有的话会产生什么影响?

我的要求是使用HtmlAgility包中的Xpath获取对象,就像WebDriver一样。

非常感谢任何帮助。

编辑1:

我们从HAP获得的XPATH也很长,比如div / div / div / div / div / a 这是西蒙爵士提供的示例的VB.Net代码

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

Dim xpathValue As String = selectedNode.XPath

然后我们从HAP获得的xpathValue是

  

/ HTML 1 /体1 /部分1 /格2 /格1 /格1 /格{{3 }} /格1 /格1 / A 1

1 个答案:

答案 0 :(得分:3)

使用XPATH时,WebDriver将始终依赖目标浏览器。从技术上讲,它只是浏览器的一个奇特的桥梁(浏览器是Firefox还是Chrome - IE最多11个不支持XPATH)

不幸的是,驻留在浏览器内存中的DOM(元素和属性结构)与您可能提供给Html Agility Pack的DOM不一样。 如果从浏览器内存中加载带有DOM内容的HAP(例如,相当于document.OuterHtml),它可能是相同的。 通常情况并非如此,因为开发人员使用HAP在没有浏览器的情况下废弃站点,因此他们从网络流(来自HTTP GET请求)或原始文件中提取它。

这个问题很容易证明。例如,如果您创建的文件只包含以下内容:

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

(没有html,没有body标签,这实际上是一个无效的html文件)

使用HAP,您可以像这样加载:

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

HAP的结构就是这样:

+table
 +tr
  +td
   'hello world'

HAP不是一个浏览器,它是一个解析器,它并不真正了解HTML规范,它只知道如何解析一堆标签并用它构建一个DOM。它不知道例如文档应该以HTML开头,并且应该包含BODY,或者在浏览器推断时,TABLE元素始终具有TBODY子项。

在Chrome浏览器中,你打开这个文件,检查它并向XPATH询问TD元素,它会报告这个:

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

因为Chrome刚刚自己完成了这项工作......如您所见,这两个系统不匹配。

请注意,如果源HTML中提供了id个属性,则故事情况会更好,例如,使用以下HTML:

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

Chrome会报告以下XPATH(它会尝试尽可能多地使用id属性):

//*[@id="hw"]

也可以在HAP中使用。但是,这并不是一直有效。例如,使用以下HTML

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

Chrome现在会向TD生成此XPATH:

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

如您所见,由于推断的TBODY,这在HAP中不再可用。

因此,最后,您不能盲目地在其他上下文中使用浏览器生成的XPATH而不是那些浏览器。在其他情况下,你必须找到其他歧视。

实际上,我个人认为它在某种程度上是一件好事,因为它会让你的XPATH更能抵抗变化。但你必须考虑: - )

现在让我们回到你的案例:)

以下C#示例控制台案例应该可以正常工作:

  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>"
  }

如果您查看流或文件的结构(甚至浏览器显示的内容,但要小心,避免使用TBODY ...),最简单的方法是

  • 找到id(就像浏览器一样)和/或
  • 在此下面找到独特的子元素或大子元素或属性,递归或不是
  • 避免过于精确的XPATH。像p/p/p/div/a/div/whatever这样的事情很糟糕

所以,在这里,在main-content id属性之后,我们只是(递归地使用//)查找具有特殊类的DIV,然后我们查看(再次递归)第一个孩子A可用。

这个XPATH应该可以在webdriver和HAP中使用。

请注意,这个XPATH也有效://div[@class='pane-content']//a但它看起来有点松散。设置id属性的脚通常是一个好主意。