【问题标题】:xpath descendant and descendant-or-self work completely differentxpath 后代和后代或自我的工作方式完全不同
【发布时间】:2015-07-29 08:51:28
【问题描述】:

我尝试在具有指定 id 的 div 的后代中查找所有秒 tds,即 22 和 222。我想到的第一个解决方案是:

//div[@id='indicator']//td[2]

但它只选择第一个表格单元格,即 22,但不会同时选择 22 和 222。 然后我用 /descendant-or-self::node()/ 替换了 // 并得到了相同的结果(显然)。但是当我删除“-or-self”时,xpath 表达式开始按预期工作

 test1 = test_tree.xpath(u"//div[@id='indicator']/descendant-or-self::node()/td[2]")
 print len(test1) #prints 1 (first one: 22)

 test1 = test_tree.xpath(u"//div[@id='indicator']/descendant::node()/td[2]")
 print len(test1) #prints 2 (22 and 222)

这里是测试 HTML

<html>
    <body>
        <div id='indicator'>
            <table>
               <tbody>
                    <tr>
                        <th>1</th>
                        <th>2</th>
                        <th>3</th>
                    </tr>
                    <tr>
                        <td>11</td>
                        <td>22</td>
                        <td>33</td>
                    </tr>
                    <tr>
                        <td>111</td>
                        <td>222</td>
                        <td>333</td>
                    </tr>
                </tbody>
            </table>
        </div>
    </body>
</html>

我想知道为什么两个表达式的工作方式不同,因为所有 td 都是 div 元素的后代,无论是否包含 div。

【问题讨论】:

  • xpath 测试仪上的所有三个 xpath 在输出中给出 2 个元素
  • 全部返回2个元素:i.imgur.com/32WRNHs.png
  • 哈哈。这是我的输出。完全相同的代码,但结果不同imgur.com/fZCL6nH
  • 另一个注意事项:Selenium IDE 也仅突出显示第一个 td[2] 而 Firebug 的 FireFinder 扩展同时显示这两个 :(
  • 我在本地服务器上复制了您的示例,其 HTML 页面包含您的 HTML 示例。我使用的是scrapy,所以我的选择器是LXML Xpath 选择器。我使用了这个 xpath 值.//div[@id='indicator']//tr/td[2],它给了我正确的结果[u'&lt;td&gt;22&lt;/td&gt;', u'&lt;td&gt;222&lt;/td&gt;']

标签: python-2.7 xpath lxml descendant descendant-or-self


【解决方案1】:

我认为您在 XPath 处理器中发现了一个错误。

【讨论】:

  • 我已向 lxml lib 错误跟踪器发布了错误报告。可能与 Python + lxml + Windows 互操作性有关
【解决方案2】:

我想我已经找到了这个问题的原因:

http://www.w3.org/TR/xpath20/#id-errors-and-opt

“在某些情况下,处理器可以在不访问正式表达式语义所暗示的所有数据的情况下确定表达式的结果。例如,过滤器表达式的正式描述表明 $s[1] 应该由检查序列$s 中的所有项目,并选择所有满足谓词position()=1 的项目。在实践中,许多实现会认识到他们可以通过获取序列中的第一项然后退出来评估此表达式。”

所以没有补救措施。它依赖于 xpath 处理器实现,但我仍然不明白为什么 //div[@id='indicator']/descendant-or-self::node()/td[2]//div[@id='indicator']/descendant::node()/td[2] 会产生不同的结果。

【讨论】:

    【解决方案3】:

    我开发了一个网页,其中包含您在问题中提供的 HTML。

    当你使用这个 xpath 时:

    .//div[@id='indicator']//tr/td[2]
    

    它按预期工作,结果是:

    [u'<td>22</td>', u'<td>222</td>']
    

    但是,according to your comment,您在问.//td[2] 何时不起作用。原因是.//td 为您提供了 DOM 中所有 td(s) 的列表。添加诸如 [2] 之类的索引将导致该 list

    中的第二个 td

    总结一下: 这些是分别应用.//td.//td[2]的结果:

    如果你想获取这些 tds 中的文本,你应该添加 /text() 如下:

    更新:

    OP 说:

    So why then //div[@id='indicator']/descendant::node()/td[2] produces ['22', '222']? According to your comment: "Adding an index such as [2] will result in the second td in that list" it should populate only ['22'].
    

    我将尝试解释这里发生了什么:

    1. descendant:node() 不等于 //
    2. 等于// 是:descendant-or-self::node()
    3. 解释在W3C specification

    我希望这段代码可以帮助你:

    【讨论】:

    • 那么为什么//div[@id='indicator']/descendant::node()/td[2] 会产生 ['22', '222']?根据您的评论:“添加诸如 [2] 之类的索引将导致该列表中的第二个 td”它应该仅填充 ['22']。
    • 好的。我再解释一下:test = test_tree.xpath(u"//div[@id='indicators_minimize']/descendant-or-self::node()")[0] print etree.tostring(test, encoding='cp866', pretty_print=True) 结果:&lt;?xml version='1.0' encoding='cp866'?&gt; &lt;div id="indicators_minimize"&gt; &lt;table&gt; &lt;tbody&gt; &lt;tr&gt; &lt;th&gt;1&lt;/th&gt; &lt;th&gt;2&lt;/th&gt; &lt;th&gt;3&lt;/th&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;11&lt;/td&gt; &lt;td&gt;22&lt;/td&gt; &lt;td&gt;33&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;111&lt;/td&gt; &lt;td&gt;222&lt;/td&gt; &lt;td&gt;333&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;/div&gt;
    • 然后:test = test_tree.xpath(u"//div[@id='indicators_minimize']/descendant::node()")[0] print etree.tostring(test, encoding='cp866', pretty_print=True) 结果:&lt;?xml version='1.0' encoding='cp866'?&gt; &lt;table&gt; &lt;tbody&gt; &lt;tr&gt; &lt;th&gt;1&lt;/th&gt; &lt;th&gt;2&lt;/th&gt; &lt;th&gt;3&lt;/th&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;11&lt;/td&gt; &lt;td&gt;22&lt;/td&gt; &lt;td&gt;33&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;111&lt;/td&gt; &lt;td&gt;222&lt;/td&gt; &lt;td&gt;333&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt;
    • 唯一的区别是第一个xml代码被封装到div标签中(因为-or-self语句)。但我们不关心 div 父级或任何其他父级深度级别,因为我们正在寻找后代。在表达式评估之后,我们不关心在哪里找到 tds:在 div 后代中或在 table 后代中(它们都包含所需的 tds)。然而结果不同。至少在我的机器上。看看这里的前两个答案:所有三个 xpath 表达式都返回相同的值。
    • @AntonKolokolcev 你的问题是什么?你已经问了两个不同的问题,我都回答了​​。请具体说明,我想在这里提供帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 2011-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多