如何在xpath中使用contains(text(),)时获取兄弟姐妹

时间:2012-02-10 02:34:59

标签: python xpath

我今天已经介绍过xpath,它似​​乎非常强大,但经过相当多的搜索后,我还没有找到如何在使用contains时检索兄弟姐妹(通过follow-sibling和previous-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元素。以同样的方式,如果不是使用跟随兄弟轴(我认为它是如何调用的),我使用//parent::*,也可以。

那么,我怎样才能让兄弟姐妹处于这种状态?

提前致谢。

4 个答案:

答案 0 :(得分: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>]

答案 1 :(得分:1)

在回答这个问题之前,有一些事情需要澄清:

  1. follow-sibling将返回所有兄弟姐妹,而不仅仅是直接兄弟姐妹。因此,如果之后有节点,那么它们也将被返回。
  2. HTML不是XML。虽然LXML会尝试为您清理源代码,但如果您不能相信传入的HTML是干净的,那么您的XPath可能会失败。例如。我相信标题标签不需要在HTML中关闭标签,因此根据源的破坏程度,LXML可能会错误地将其作为子节点,这可能会破坏XPath
  3. 标题不能包含子元素,这可能会影响LXML清理它的方式(例如在它们之间添加正文标记等)。
  4. 在XML编辑器中对此进行测试表明您的XPath是有效的,但是在LXML中进行测试时我得到的元素不足,这可能意味着它正在改变XML(但我没有检查)。

    如果XPath是这项工作的工具,我建议重新考虑,特别是如果你试图用它来搜索网页或类似的东西。

    您也可以考虑重写XPath语句,以便它更具可读性。

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

    这说:找到任何在文本中包含“some text”的元素,然后获得下一个兄弟姐妹。

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

    虽然这样说:找到我的第一个兄弟的文本包含“某些文字”的元素。

    这可能是一个样式问题,但我发现后者更具可读性。

答案 2 :(得分: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应该是相同的。

答案 3 :(得分:0)

这里的关键是你的XPath正在查看由HTML5解析器创建的树,而不是XML解析器。 HTML5解析器在树中创建在源中不明确的节点:实际上,它们修复无效的HTML并将其转换为有效的HTML。这会影响任何导航HTML树的尝试,无论您使用的是XPath,JQuery还是直接的DOM API。