【问题标题】:XPath predicate to compare selected node with context node?XPath谓词将选定节点与上下文节点进行比较?
【发布时间】:2021-01-30 21:40:18
【问题描述】:

假设我有以下简化的嵌套 HTML 列表:

<ol>
  <li>Item 1</li>
  <li>Item 2
    <ul>
      <li>Item 2 1</li>
    </ul>
  </li>
  <li>Item 3</li>
</ol>

现在我想在迭代列表项时访问每个文本节点:

for li in xml.xpath(".//li"):
    for t in li.xpath(".//text()"):
        print(t)

但是,这会打印两次 Item 2 1,因为该文本节点是两个 li 节点的后代。因此,我只想选择其祖先 li 是当前/上下文列表项的那些文本节点,以避免在嵌套列表项中多选文本节点。类似的东西

li.xpath(".//text[ancestor::li[1] == .]")

但这是一个无效的表达式。

我该怎么做? (这是使用 lxml,它基于实现 XPath 1.0 的 libxml2)。

【问题讨论】:

    标签: python xpath lxml xpath-1.0


    【解决方案1】:

    如果我理解正确,这应该可以:

    for t in xml.xpath('//li[text()]'):
        print(t.text.strip()
    

    输出:

    Item 1
    Item 2
    Item 2 1
    Item 3
    

    【讨论】:

      【解决方案2】:

      首先,可以注意到下面的 XPath 1.0 表达式:

      .//text()
      

      descendant-or-self::text() 的快捷方式(一些“语法糖”)-the thirteen XPath 1.0 axes 之一。

      因此,如果您只想获取“与当前节点处于同一级别”的文本节点(实际上是其直接子节点),您应该只使用轴child::text()。这是默认轴顺便说一句,所以你可以写text()

      依靠您问题中的示例:

      #!/usr/bin/env python3
      from lxml import etree
      with open('./a.xml') as data:
          xml = etree.parse(data)
          for li in xml.xpath(".//li"):
              print(li.xpath("text()"))
      

      会输出

      ['Item 1']
      ['Item 2\n    ', '\n  ']
      ['Item 2 1']
      ['Item 3']
      

      【讨论】:

      • text() 不适用于&lt;li&gt;Some &lt;em&gt;text&lt;/em&gt;&lt;ol&gt;…&lt;/ol&gt;&lt;/li&gt;,因为它不会选择li 后代的文本节点。
      • 但这就是你想要的,不是吗?
      • 鉴于外部循环将进入这些嵌套的li 节点...
      猜你喜欢
      • 1970-01-01
      • 2017-08-01
      • 1970-01-01
      • 2015-07-09
      • 1970-01-01
      • 2010-11-04
      • 2013-08-12
      • 2013-05-21
      • 2012-08-21
      相关资源
      最近更新 更多