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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (15)

这是为了给所有类似的问题提供一个规范的问答(但是很明确的问题是一个接近的目标候选人),每周出现一次或两次。

我正在开发一个需要解析带有表格的网站的应用程序。由于派生用于抓取网页的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 -->

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

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

工作在任何XPath测试仪插件很好,但不是我自己的应用程序(没有找到结果)。如果我减少查询//table[@id],它再次工作。

出了什么问题?

提问于
用户回答回答于

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

检查你被卡住的表是否确实不包含<tbody/>元素(请参见最后一段)。如果是这样,你可能会遇到另一种问题。

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

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

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

这是一个相当脏的解决方案,可能会失败的嵌套表(可以跳转到内部表)。我只会在非常罕见的情况下推荐这一点。

/tbody后代或自己的步骤替换轴步骤:

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

解决方案3:允许两个输入有和没有<tbody/>标记

如果您不确定您的表格或在“HTML源代码”和DOM上下文中使用查询,并且不希望/不能使用解决方案2中的攻击,提供替代查询(对于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]
用户回答回答于

确保HTML源始终具有<tbody>带正则表达式的标记

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

扫码关注云+社区