Html Agility Pack 无法使用 xpath 找到元素,但它与 WebDriver 一起工作正常

Html Agility Pack cannot find element using xpath but it is working fine with WebDriver

我已经看过这些问题 1 and 2 但不适合我。

我正在为对象创建 Xpath,它在 WebDriver 中运行良好,但是当尝试使用 HtmlAgilityPack select 节点时,它在某些情况下不起作用。

我正在使用最新的 HtmlAgilityPack 1.4.9

例如Here是一个页面。

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

//section[@id='main-content']/div2/div/div/div/div/div/p1/a

同图另一个物体

它的 xpath 是

//section[@id='main-content']/div2/div/div/div/div/div/ul/li2/a

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

对于我尝试的第一个

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

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

是否有需要从 ElementFlags 中删除的特定标签列表?如果有那么它的影响是什么?

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

任何帮助将不胜感激。

编辑 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就是

/html1/body1/section1/div2/div1/div1/div1/div1/div1/a1

WebDriver 在使用 XPATH 时将始终依赖于目标浏览器。从技术上讲,它只是连接浏览器的一个花哨的桥梁(无论浏览器是 Firefox 还是 Chrome - IE up to 11 不支持 XPATH)

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

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

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

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

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

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

HAP 的结构就是这样:

+table
 +tr
  +td
   'hello world'

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

但是在 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 中不再可用。

所以,最后,您不能只是在那些浏览器之外的其他上下文中盲目地使用 browsers-generated 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(就像浏览器一样)and/or
  • 查找此下方的唯一 child 或宏大 child 元素或属性,递归或非递归
  • 避免过于精确的 XPATH。像 p/p/p/div/a/div/whatever 这样的东西是不好的

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

这个 XPATH 应该在 webdriver 和 HAP 中工作。

请注意,此 XPATH 也有效://div[@class='pane-content']//a 但我觉得它有点松散。踏上 id 属性通常是个好主意。