为什么我的XPath查询(抓取HTML表)仅适用于Firebug,而不适用于我正在开发的应用程序?

Jen*_*rat 20 html firebug xpath dom google-chrome-devtools

这是为了向所有相似的(但是太具体的问题,成为一个近距离目标候选人)提供规范的问答,每周弹出一到两次.

我正在开发一个应用程序,需要解析一个包含表格的网站.由于派生用于抓取网页的XPath表达式是无聊且容易出错的工作,我想使用FirebugXPath提取器功能(或其他浏览器中的类似工具).

示例输入如下所示:

<!-- snip -->
<table id="example">
  <tr>
    <th>Example Cell</th>
    <th>Another one</th>
  </tr>
  <tr>
    <td>foobar</td>
    <td>42</td>
  </tr>
</table>
<!-- snip -->
Run Code Online (Sandbox Code Playgroud)

我想提取第一个数据单元格("foobar").Firebug提出了XPath表达式

//table[@id="example"]/tbody/tr[2]/td[1]
Run Code Online (Sandbox Code Playgroud)

适用于任何XPath测试器插件,但不适用于我自己的应用程序(未找到结果).如果我将查询减少到//table[@id],它会再次起作用.

出了什么问题?

Jen*_*rat 42

问题:DOM需要<tbody/>标签

Firebug,Chrome的开发人员工具,JavaScript中的XPath函数和其他工作在DOM上,而不是基本的HTML源代码.

HTML的DOM要求页脚标记中包含未包含在页脚(<thead/>,<tfoot/>)的表头中的所有表行<tbody/>.因此,如果在解析(X)HTML时缺少此标记,则浏览器会添加此标记.例如,微软的DOM文档

tbody元件暴露的所有表,即使表没有明确定义tbody的元素.

stackoverflow的另一个答案有一个深入的解释.

另一方面,HTML不一定要求使用该标记:

TBODY开始标签总是需要当表包含只有一个表的身体和没有表头或脚的部分除外.

大多数XPath处理器都处理原始XML

除了JavaScript之外,大多数XPath处理器都处理原始XML,而不是DOM,因此不添加<tbody/>标记.此外,像这样的HTML解析器库只输出XHTML,而不是"DOM-HTML".

这是Stackoverflow上发布的PHP,Ruby,Python,Java,C#,Google Docs(电子表格)和许多其他问题的常见问题.Selenium在浏览器中运行并在DOM上运行 - 因此它不受影响!

再现问题

将Firebug(或Chrome的开发工具)显示的源与右键单击并选择"显示页面源"(或浏览器中调用的任何内容) - 或curl http://your.example.org在命令行中使用的源进行比较.后者可能不会包含任何<tbody/>元素(它们很少使用),Firebug将始终显示它们.


解决方案1:删除/tbody轴步骤

检查你所困的表是否真的不包含<tbody/>元素(参见最后一段).如果是这样,你可能会遇到另一种问题.

现在删除/tbody轴步骤,以便您的查询看起来像

//table[@id="example"]/tr[2]/td[1]
Run Code Online (Sandbox Code Playgroud)

解决方案2:跳过<tbody/>标签

这是一个相当脏的解决方案,可能会失败嵌套表(可以跳转到内部表).我只会在极少数情况下推荐这个.

/tbody通过后代或自我步骤替换轴步骤:

//table[@id="example"]//tr[2]/td[1]
Run Code Online (Sandbox Code Playgroud)

解决方案3:允许带和不带<tbody/>标签的输入

如果您不确定您的表或在"HTML源"和DOM上下文中使用查询; 并且不希望/不能使用解决方案2中的hack,提供替代查询(对于XPath 1.0)或使用"可选"轴步骤(XPath 2.0及更高版本).

  • XPath 1.0:
    //table[@id="example"]/tr[2]/td[1] | //table[@id="example"]/tbody/tr[2]/td[1]
  • XPath 2.0://table[@id="example"]/(tbody, .)/tr[2]/td[1]