【问题标题】:Find all elements by partially matched tag in Python ElementTree using XPath使用 XPath 在 Python ElementTree 中通过部分匹配的标记查找所有元素
【发布时间】:2020-06-05 21:06:53
【问题描述】:

我正在尝试在 XHTML ElementTree 中查找所有标题元素,我想知道是否有任何方法可以使用 XPath 做到这一点。

<body>
  <h1>title</h1>
  <h2>heading 1</h2>
  <p>text</p>
  <h3>heading 2</h3>
  <p>text</p>
  <h2>heading 3</h2>
  <p>text</p>
</body>

我的目标是让所有标题元素按顺序排列,天真的解决方案行不通:

for element in tree.iterfind("h*"):
  foo(element)

因为它们应该是有序的,所以我不能单独遍历每个标题元素

headings = {f"h{n}" for n in range(1, 6+1)}

for heading in headings:
  for element in tree.iterfind(heading):
    foo(element)

(但 for element in filter(lambda el: el.tag in headings, tree.iterfind()) 有效)

我不能使用正则表达式,因为它在 cmets 上中断(不使用字符串标签)

import re
pattern = re.compile("^h[1-6]$")
is_heading = lambda el: pattern.match(el.tag)

for element in filter(is_heading, tree.iterfind()):
  foo(element)

(但 is_heading = lambda el: isinstance(el.tag, str) and pattern.match(el.tag) 有效)

没有一个解决方案特别优雅,所以我想知道是否有更好的方法可以使用 xpath 按顺序查找所有标题元素?

【问题讨论】:

  • XPath 支持在 ElementTree 中受到限制;你能用lxml吗?
  • 以下任何答案是否有帮助或您仍有问题?

标签: python-3.x xml xpath elementtree


【解决方案1】:

像这样:

//*[self::h1 or self::h2 or self::h3]

【讨论】:

  • 我假设您的意思是 * 而不是 .
  • 我知道 ElementTree 支持一些无效的 xpath,所以我确实尝试了它并得到了 SyntaxError: cannot use absolute path on element 错误(像 OP 一样使用 iterfind)。也许您可以扩展您的答案,这样我们就不必猜测您如何使无效的 xpath 工作?
  • 这取决于 xpath 版本。尝试使用 xpath3。更改了表达式以使用 xpath1
  • 啊...ElementTree 仅支持 XPath 1.0 的有限子集。所以我没有想到 XPath 3.0。 (另外,不幸的是,self::axis 也不支持。)
【解决方案2】:

如果你可以使用lxml,你可以使用union operator |...

from lxml import etree

xml = """
<body>
  <h1>title</h1>
  <h2>heading 1</h2>
  <p>text</p>
  <h3>heading 2</h3>
  <p>text</p>
  <h2>heading 3</h2>
  <p>text</p>
</body>
"""

tree = etree.fromstring(xml)

for elm in tree.xpath("//h1|//h2|//h3"):
    print(elm.text)

打印输出...

title
heading 1
heading 2
heading 3

如果您愿意,lxml 还允许您使用 self:: 轴,就像另一个答案中提到的那样。

【讨论】:

    【解决方案3】:

    另一种方法。

    from simplified_scrapy import SimplifiedDoc,req,utils
    html ='''
    <body>
      <h1>title</h1>
      <h2>heading 1</h2>
      <p>text</p>
      <h3>heading 2</h3>
      <p>text</p>
      <h2>heading 3</h2>
      <p>text</p>
    </body>'''
    doc = SimplifiedDoc(html)
    hs = doc.getElementsByReg('h[1-9]')
    print(hs.text)
    

    结果:

    ['title', 'heading 1', 'heading 2', 'heading 3']
    

    【讨论】:

      【解决方案4】:

      这个 XPath 也应该可以工作:

      '//*[starts-with(name(), "h") and not(translate(substring(name(),string-length(name())), "0123456789", ""))]'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-10
        • 2020-02-06
        • 2020-05-07
        • 1970-01-01
        • 2023-03-18
        • 2020-07-20
        • 1970-01-01
        相关资源
        最近更新 更多