首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么我的XPath查询(抓取HTML表)只能在Firebug中工作,而不能在我正在开发的应用程序中工作?

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

Stack Overflow用户
提问于 2013-08-15 03:53:01
回答 2查看 16.5K关注 0票数 21

这意味着为每周出现一到两次的所有类似(但过于具体的问题,而不是目标候选人)提供一个规范的问答。

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

示例输入如下所示:

代码语言:javascript
复制
<!-- 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表达式

代码语言:javascript
复制
//table[@id="example"]/tbody/tr[2]/td[1]

哪个在任何XPath测试器插件中都工作得很好,但我自己的应用程序(找不到结果)就不行。如果我将查询缩减为//table[@id],它将再次工作。

出什么问题了?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-08-15 03:53:01

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

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

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

即使表没有显式定义tbody元素,也会为所有表公开tbody元素。

有一个in-depth explanation in another answer on stackoverflow

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

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

大多数XPath处理器都在原始XML上工作。

除JavaScript外,大多数XPath处理程序都处理原始的XML,而不是DOM,因此不会添加<tbody/>标记。此外,像tag-souphtmltidy这样的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轴步骤,这样您的查询将如下所示

代码语言:javascript
复制
//table[@id="example"]/tr[2]/td[1]

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

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

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

代码语言:javascript
复制
//table[@id="example"]//tr[2]/td[1]

解决方案3:允许使用和不使用<tbody/>标记的输入

如果您事先不确定您的表或在"HTML“和DOM上下文中都使用了查询,并且不想/不能使用解决方案2中的技巧,那么可以提供另一个查询(对于XPath 1.0)或使用”可选的“axis步骤(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]
票数 45
EN

Stack Overflow用户

发布于 2015-01-31 02:33:42

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

在解析之前,获取字符串形式的html。使用正则表达式插入缺少的<tbody></tbody>标记,然后将其加载回DOMDocument对象。

延斯·埃拉特给出了一个很好的解释,但这里是

解决方案4:确保HTML源始终具有带正则表达式的<tbody>标记

代码语言:javascript
复制
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);

只有正则表达式:

代码语言:javascript
复制
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将在必要的地方始终具有<tbody>标记。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18241029

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档