为啥我的 XPath 查询(抓取 HTML 表)只能在 Firebug 中工作,而不能在我正在开发的应用程序中工作?

Posted

技术标签:

【中文标题】为啥我的 XPath 查询(抓取 HTML 表)只能在 Firebug 中工作,而不能在我正在开发的应用程序中工作?【英文标题】:Why does my XPath query (scraping HTML tables) only work in Firebug, but not the application I'm developing?为什么我的 XPath 查询(抓取 HTML 表)只能在 Firebug 中工作,而不能在我正在开发的应用程序中工作? 【发布时间】:2013-08-16 22:54:16 【问题描述】:

这旨在为每周出现一两次的所有类似(但过于具体的问题而不是密切的目标候选人)提供规范的问答。

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

示例输入如下所示:

<!-- snip -->
<table id="example">
  <tr>
    <th>Example Cell</th>
    <th>Another one</th>
  </tr>
  <tr>
    <td>foobar</td>
    <td>42</td>
  </tr>
</table>
<!-- snip -->

我想提取第一个数据单元格(“foobar”)。 Firebug 提出 XPath 表达式

//table[@id="example"]/tbody/tr[2]/td[1]

在任何 XPath 测试器插件中都可以正常工作,但在我自己的应用程序中却不行(未找到结果)。如果我将查询减少到//table[@id],它会再次起作用。

怎么了?

【问题讨论】:

也许值得一提的是,在 &lt;script&gt; &lt;/script&gt; 标记内测试这些 xpath 查询并不是一个好主意,它们被插入到 &lt;body&gt; 后面,因为它会失败(元素还不存在) . ***.com/questions/14028959/… 今天我还根据***.com/a/25949484/367456 进行了一些讨论,这与Table 无关,而是与浏览器Xpath 相关:似乎Firefox 接受大写的元素和属性名称。 DOMDocument xpath 需要那些小写的(不是这个参考问题中的问题,但我想交叉链接它,因为我第一次看到它,这是一个伟大的倡议!)。 【参考方案1】:

问题:DOM 需要 &lt;tbody/&gt; 标签

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

html 的 DOM 要求所有不包含在表头页脚(&lt;thead/&gt;&lt;tfoot/&gt;)中的表行都包含在表体标签 &lt;tbody/&gt; 中。因此,如果在解析 (X)HTML 时缺少此标记,浏览器会添加此标记。例如,Microsoft's DOM documentation 表示

tbody 元素对所有表都公开,即使该表没有明确定义 tbody 元素。

有一个in-depth explanation in another answer on ***。

另一方面,HTML does not necessarily require that tag to be used:

TBODY 开始标记始终是必需的,除非表格仅包含一个表格主体且没有表格头或脚部分。

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

除了 JavaScript,大多数 XPath 处理器都处理原始 XML,而不是 DOM,因此不要添加 &lt;tbody/&gt; 标记。还有像tag-soup和htmltidy这样的HTML解析器库只输出XHTML,而不是“DOM-HTML”。

这是 *** 上发布的常见问题,适用于 php、Ruby、Python、Java、C#、Google Docs(电子表格)和许多其他问题。 Selenium 在浏览器中运行并在 DOM 上运行——因此不受影响!

重现问题

将 Firebug(或 Chrome 的开发工具)显示的源代码与您通过右键单击并选择“显示页面源代码”(或在浏览器中调用的任何名称)或在命令行。后者可能不包含任何 &lt;tbody/&gt; 元素(它们很少使用),Firebug 将始终显示它们。


解决方案1:删除/tbody Axis Step

检查您遇到的表格是否真的不包含&lt;tbody/&gt; 元素(请参阅最后一段)。如果是这样,您可能遇到了另一种问题。

现在删除 /tbody 轴步骤,这样您的查询将如下所示

//table[@id="example"]/tr[2]/td[1]

解决方案 2:跳过 &lt;tbody/&gt; 标签

这是一个相当肮脏的解决方案,并且对于嵌套表可能会失败(可以跳转到内部表)。我只会在极少数情况下建议这样做。

/tbody 轴步骤替换为后代或自身步骤:

//table[@id="example"]//tr[2]/td[1]

解决方案 3:允许带和不带 &lt;tbody/&gt; 标签的输入

如果您事先不确定您的表格或在“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]

【讨论】:

除了上面所说的,对于我在这些场景中的刮板,我有一个“skipFirstRow”标志,它实际上工作得很好(对于我正在刮的页面)。 我已经搜索了 4 个小时的解决方案,因为我想要从某个站点获得的数据不想成为我的。所有值都可以通过它们的 xpaths 轻松获得,但是其中一个表返回了 error,解决方案是删除 tbody 并用额外的 / 替换它.【参考方案2】:

刚刚遇到同样的问题。我几乎写了一个递归函数来检查每个 tbody 标签是否存在并以这种方式遍历 dom,然后我记得我知道正则表达式。 :)

在解析之前,将 html 作为字符串获取。使用正则表达式插入缺少的 &lt;tbody&gt;&lt;/tbody&gt; 标签,然后将其加载回您的 DOMDocument 对象。

Jens Erat 给出了很好的解释,但这里是

解决方案 4:确保 HTML 源代码始终包含带有正则表达式的 &lt;tbody&gt; 标记

JavaScript
    var html = '<html><table><tr><td>foo</td><td>bar</td></tr></table></html>';
    html.replace(/(<table([^>]+)?>([^<>]+)?)(?!<tbody([^>]+)?>)/g,"$1<tbody>").replace(/(<(?!(\/tbody))([^>]+)?>)(<\/table([^>]+)?>)/g,"$1</tbody>$4");

PHP
    $html = $dom->saveHTML();
    $html = preg_replace(array('/(<table([^>]+)?>([^<>]+)?)(?!<tbody([^>]+)?>)/','/(<(?!(\/tbody))([^>]+)?>)(<\/table([^>]+)?>)/'),array('$1<tbody>','$1</tbody>$4'),$html);
    $dom->loadHTML($html);

只是正则表达式:

matches `<table>` tag with whatever else junk inside the tag and between this and the next tag if the next tag is NOT `<tbody>` also with stuff inside the tag

    /(<table([^>]+)?>([^<>]+)?)(?!<tbody([^>]+)?>)/

replace with

    $1<tbody>

the $1 referencing the captured `<table>` tag with contents.
Do the same for the closing tag like this:

    /(<(?!(\/tbody))([^>]+)?>)(<\/table([^>]+)?>)/

replace with

    $1</tbody>$4

这样 dom 将在必要时始终使用 &lt;tbody&gt; 标签。

【讨论】:

不知怎的,这个答案立刻让我想起了***.com/a/1732454/5114081 @Alex 这是一个很棒的帖子。但它真的适用于这个案例吗?正则表达式用于在字符串被实际的 xml 解析器解析之前插入可能丢失的部分。可以通过搜索、检查、插入、移动和迭代对它解析成的对象执行相同的操作,但是这似乎更快,因为计算机不关心位代表什么。请注意,我在这里可能完全错了,如果有人可以展示这种方法导致安全问题、意外行为等,请提供解释或示例,以便我们都可以从中学习。 就个人而言,我想不出在这里使用正则表达式可能导致意外行为的示例,否则我会提到它。这并不是为了不必要地批评你的帖子,我只是想提醒一下,使用正则表达式可以(至少在理论上)带来一些后备。

以上是关于为啥我的 XPath 查询(抓取 HTML 表)只能在 Firebug 中工作,而不能在我正在开发的应用程序中工作?的主要内容,如果未能解决你的问题,请参考以下文章

如果不包含某些字符串,则替换某些子值?还是重写 XPATH 查询?网站抓取

Python - for循环,它产生的抓取数据每页只循环一次

Scrapy使用xpath抓取ul类是行不通的

使用 XML 包将 html 表抓取到 R 数据帧中

尝试使用 BeautifulSoup 从我的代码中使用 Xpath 进行网络抓取 [重复]

为啥我的 XPath 表达式在 XML 文档中找不到新添加的节点?