【问题标题】:How to get siblings when using contains(text(), ) in xpath在 xpath 中使用 contains(text(), ) 时如何获取兄弟姐妹
【发布时间】:2012-02-10 02:34:59
【问题描述】:

我今天被介绍给 xpath,它似​​乎非常强大,但经过相当多的搜索,我还没有找到在使用 contains 时如何检索兄弟姐妹(通过以下兄弟和前兄弟):

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 axis(我认为这就是它的名称),而是使用//parent::*,也可以。

那么,在这种情况下,我怎样才能得到兄弟姐妹呢?

提前致谢。

【问题讨论】:

    标签: python xpath


    【解决方案1】:

    你有有趣的 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>]
    

    【讨论】:

    • 现在我发现我的问题在于没有 的 。由于您将其更改为身体标签,因此一切正常。坦率地说,我不知道该怎么做。
    • 顺便说一句,另一个问题可能是 中有 ,但这真的很奇怪,因为 中的 是允许的,是不是吗? (<a href="/default/index/tourl?u=aHR0cDovL3d3dy53M3NjaG9vbHMuY29tL3RhZ3MvdGFnX3RpdGxlLmFzcA%3D%3D" rel="nofollow" target="_blank">w3schools.com/tags/tag_title.asp</a>)
    • @RobertSmith,您的 html 很好,您不必更改它。请参阅我的更新答案。
    • 哦,非常感谢。我真的需要看看lxml。像您一样使用 lxml.etree.fromstring(text) 可以避免任何解析(我希望如此),但是 lxml.html.fromstring(text) 会进行解析。
    • @RobertSmith,我猜lxml.html.fromstring(text)lxml.etree.fromstring(text, parser=lxml.etree.HTMLParser()) 的快捷方式。这个页面的底线是:“你的 xpath 表达式从一开始就是正确的”。
    【解决方案2】:

    在回答这个问题之前有几点需要澄清:

    1. following-sibling 将返回所有跟随的兄弟姐妹,而不仅仅是直接的兄弟姐妹。所以如果后面有节点,那么它们也会被返回。
    2. HTML 不是 XML。虽然 LXML 会尝试为您清理源代码,但如果您不能相信传入的 HTML 是干净的,那么您的 XPath 可能会失败。例如。我相信标题标签不需要 HTML 中的结束标签,因此根据源代码的损坏程度,LXML 可能会错误地将
    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 ]]
    

    而这说:找到我的第一个上一个兄弟元素的文本包含“一些文本”。

    这可能是风格问题,但我发现后者更具可读性。

    【讨论】:

    • 那么//*[contains(text(),'some_text')]/following-sibling::*[1]呢?最初的 html 不起作用,因为 lxml 解析器添加了 &lt;body&gt; 并将 &lt;h2&gt; 移动到那里,将 &lt;title&gt; 单独留在 &lt;head&gt; 中,没有任何兄弟姐妹。
    • 哦,我不知道 lxml 正在更改源。我不觉得这是可取的。我想要的只是获取包含'some_text'的节点并稍微遍历树。也许 lxml 不是适合这项工作的工具,虽然我不希望处理 HTML 如此破碎,但很高兴知道。有没有更好的解决方案以这种方式解析但没有这些问题?
    • @MishaAkovantsev 顺便说一句,感谢您的澄清。这就解释了为什么其他标记有效,而我的却无效。
    • @Robert Smith LXML 会更改 HTML,因为它试图从源代码生成“有效”HTML,并且在 &lt;head&gt;&lt;title &gt; 中包含 &lt;h2&gt; 无效。根据您要执行的操作,BeautifulSoup 也可能是一种选择,但如果您可以控制传入的源 LXML 应该可以正常工作。
    • @LegoStormtroopr 现在我明白了。但我认为 是有效的 (<a href="/default/index/tourl?u=aHR0cDovL3d3dy53M3NjaG9vbHMuY29tL3RhZ3MvdGFnX3RpdGxlLmFzcA%3D%3D" rel="nofollow" target="_blank">w3schools.com/tags/tag_title.asp</a>)。不是吗。。 BeautifulSoup 效果很好,但我没有找到对 xpath 的支持,我需要能够匹配属性和 TextNodes 并从那里遍历树。我认为没有 xpath 是不可能的。
    【解决方案3】:
    <?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 应该是一样的。

    【讨论】:

    • 嗯,是不是和我用的一模一样://*[contains(text(),'some_text')]/following-sibling::*?
    • 嘿,我想你是对的。我在另一段 XML 上对其进行了一些测试,然后返回到您的 XML,并得出了与您相同的字符串,但它对我有用。可能是因为我将它视为 XML,而不是 HTML,并且没有 StormTrooper 和其他人提到的 LXML 问题。 (我自己不会意识到这一点。这周刚开始使用 xpath,不要在 py 中编码)
    • 这可能是真的。 xml 标签使一切变得不同。谢谢!
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      • 2019-08-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多