如何匹配文本节点然后使用 XPath 跟随父节点

Posted

技术标签:

【中文标题】如何匹配文本节点然后使用 XPath 跟随父节点【英文标题】:How to match a text node then follow parent nodes using XPath 【发布时间】:2010-10-10 13:38:10 【问题描述】:

我正在尝试使用 XPath 解析一些 html。按照下面的简化 XML 示例,我想匹配字符串 'Text 1',然后获取相关 content 节点的内容。

<doc>
    <block>
        <title>Text 1</title>
        <content>Stuff I want</content>
    </block>

    <block>
        <title>Text 2</title>
        <content>Stuff I don't want</content>
    </block>
</doc>

我的 Python 代码不稳定:

>>> from lxml import etree
>>>
>>> tree = etree.XML("<doc><block><title>Text 1</title><content>Stuff 
I want</content></block><block><title>Text 2</title><content>Stuff I d
on't want</content></block></doc>")
>>>
>>> # get all titles
... tree.xpath('//title/text()')
['Text 1', 'Text 2']
>>>
>>> # match 'Text 1'
... tree.xpath('//title/text()="Text 1"')
True
>>>
>>> # Follow parent from selected nodes
... tree.xpath('//title/text()/../..//text()')
['Text 1', 'Stuff I want', 'Text 2', "Stuff I don't want"]
>>>
>>> # Follow parent from selected node
... tree.xpath('//title/text()="Text 1"/../..//text()')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "lxml.etree.pyx", line 1330, in lxml.etree._Element.xpath (src/
lxml/lxml.etree.c:14542)
  File "xpath.pxi", line 287, in lxml.etree.XPathElementEvaluator.__ca
ll__ (src/lxml/lxml.etree.c:90093)
  File "xpath.pxi", line 209, in lxml.etree._XPathEvaluatorBase._handl
e_result (src/lxml/lxml.etree.c:89446)
  File "xpath.pxi", line 194, in lxml.etree._XPathEvaluatorBase._raise
_eval_error (src/lxml/lxml.etree.c:89281)
lxml.etree.XPathEvalError: Invalid type

这在 XPath 中可行吗?我需要用不同的方式表达我想做的事吗?

【问题讨论】:

【参考方案1】:

你想要吗?

//title[text()='Text 1']/../content/text()

【讨论】:

呃,真的很简单!我现在选择 text() 属性有点道理。 也可以使用 //block[title='Text 1']/content 获取相关的内容节点 @Dror:现在知道这一点很有用。【参考方案2】:

使用

string(/*/*/title[. = 'Text 1']/following-sibling::content)

与目前接受的 Johannes Weiß 解决方案相比,这代表了至少两个改进

    非常昂贵的缩写“//”(通常会导致扫描整个 XML 文档)被避免,因为它应该在任何时候XML 文档是预先知道的。

    没有返回父节点(避免了定位步骤“/..”)

【讨论】:

公平的改进,我的实际文档是 HTML 并且“标题”部分嵌套了大约五层深,所以我必须返回大约五个父母才能进入“内容”区域。我会牢记第一点,尽管这对于肮脏的黑客来说几乎没有什么区别。 /*/*/ 是做什么的?我正在一个相当大的文档上尝试它,它似乎和//一样慢。 @dentarg: /*/* 选择文档顶部元素的所有子元素。它比//someName 更快,它遍历整个文档并选择每个名为"someName" 的元素。在这个答案中,我们可以使用更有效的表达式:string(/*/*/title[. = 'Text 1'][1]/following-sibling::content) 答案中的表达式应该不会降低效率,给定一个优化良好的 XPath 处理器——因为每当string() 函数提供一个参数时节点集,它只产生这个节点集的第一个节点的字符串值。

以上是关于如何匹配文本节点然后使用 XPath 跟随父节点的主要内容,如果未能解决你的问题,请参考以下文章

XPath 1.0:使用当前节点的父节点的属性值来寻找另一个匹配的节点

XPath如何定位dom节点

通过 XPATH 查找没有特定父节点的节点

selenium+xpath 文本信息定位

如何使用simpleXML(php)通过xpath获取父节点

Xpath:如何结合这两个子节点得到父节点?