如何在XPath中使用contains(text(), )获取兄弟节点

3 投票
4 回答
10223 浏览
提问于 2025-04-17 12:44

今天我接触到了xpath,感觉它非常强大。不过我搜索了很久,还是没找到在使用contains的时候,怎么获取兄弟节点(通过following-sibling和preceding-sibling)。

text = """
<html>
  <head>
    <title>This tag includes 'some_text'</title>
    <h2>A h2 tag</h2>
  </head>
</html>
"""

import lxml.html
doc = lxml.html.fromstring(text)
a = doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")

这个方法返回的是[]。当然,我期待的结果是能获取到h2标签。

不过,使用*[contains(text(),'name')]可以如我所愿地获取到title元素。同样,如果我不使用following-sibling这个轴(我想这就是它的叫法),而是用//parent::*,也能成功。

那么,在这种情况下,我该怎么获取兄弟节点呢?

提前谢谢大家。

4 个回答

1
<?xml version="1.0" ?>
  <html>
    <head>
      <title>This tag includes 'some_text'</title>
      <h2>A h2 tag</h2>
    </head>
  </html>
//*[contains(text(),'some_text')]/following-sibling::*
Array
(
    [0] => SimpleXMLElement Object
        (
            [0] => A h2 tag
        )

)

我使用了PHP的SimpleXMLElement,不过xpath应该是一样的。

1

在回答这个问题之前,有几个事情需要先说明:

  1. 使用following-sibling会返回所有后面的兄弟节点,而不仅仅是紧挨着的一个。所以如果在这个节点后面还有其他节点,它们也会被返回。
  2. HTML和XML是不同的。虽然LXML会帮你清理源代码,但如果你不能保证输入的HTML是干净的,那么你的XPath可能会出错。例如,我认为在HTML中,title标签不需要闭合标签,所以根据源代码的情况,LXML可能会错误地把某个节点当作title的子节点,这样就会导致XPath出问题。
  3. 标题标签不能有子元素,这可能会影响LXML的清理方式(比如在它们之间添加一个body标签等等)。

在XML编辑器中测试这个XPath显示它是有效的,但我在LXML中测试时却缺少元素,这可能意味着它以某种方式改变了XML(不过我没有去检查)。

我建议你重新考虑一下XPath是否适合这个任务,特别是如果你是想用它来抓取网页或类似的内容。

你也可以考虑重写XPath语句,让它更易读一些。

//*[contains(text(),'some_text')]/following-sibling::*

这个表达的意思是:找出任何包含“某些文本”的元素,然后获取它的所有后续兄弟节点。

//*[preceding-sibling::*[position()=1 and contains(text(),'some_text') and ]]

而这个表达的意思是:找出第一个前面的兄弟节点包含“某些文本”的元素。

这可能是风格问题,但我觉得后者更容易理解。

7

你有个有趣的HTML示例。

import lxml

text = """                                                       
<html>
  <body>
    <span>This tag includes 'some_text'</span>
    <h2>A h2 tag</h2>
  </body>
</html>
"""

doc = lxml.etree.fromstring(text, parser=lxml.etree.HTMLParser())
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
# [<Element h2 at 102eee100>]

doc = lxml.html.fromstring(text)
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*")
# [<Element h2 at 102f6f188>]

更新:

在这里,我没有使用html解析器和它的验证规则,而是把输入当作随机的XML来处理:

text = """                       
<html>
  <head>
    <title>This tag includes 'some_text'</title>
    <h2>A h2 tag</h2>
  </head>
</html>
"""
doc = lxml.etree.fromstring(text)
doc.xpath("//*[contains(text(),'some_text')]/following-sibling::*[1]")
# [<Element h2 at 102eeef70>]

撰写回答