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
属性通常是个好主意。
我已经看过这些问题 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
属性通常是个好主意。