【问题标题】:how do I formulate this xpath expression?如何制定这个 xpath 表达式?
【发布时间】:2012-07-11 12:15:18
【问题描述】:

给定以下 div 元素

<div class="info">
    <a href="/s/xyz.html" class="title">title</a>
    <span class="a">123</span>
    <span class="b">456</span>
    <span class="c">789</span>
</div>

我想用“b”类检索跨度的内容。但是,我想解析的一些 div 缺少后两个跨度(“b”类和“c”类)。对于这些 div,我想要“a”类的 span 的内容。是否可以创建一个选择它的 XPath 表达式?

如果不可能,是否可以创建一个选择器来检索 div 的全部内容?即检索

<a href="/s/xyz.html" class="title">title</a>
<span class="a">123</span>
<span class="b">456</span>
<span class="c">789</span>

如果我能做到,我可以使用正则表达式来查找我想要的数据。 (我可以选择 div 中的文本,但我也不确定如何选择标签。仅文本产生 123456789。)

【问题讨论】:

    标签: xpath


    【解决方案1】:

    更高效——不需要联合

       //div/span
              [@class='b'
               or
                 @class='a'
                and
                 not(parent::*[span[@class='b']])
               ]
    

    作为两个绝对“// 表达式”联合的表达式(如下所示),通常执行两个完整的文档树遍历,然后联合操作按文档顺序执行重复数据删除和排序——所有这些都可以除非 XPath 处理器具有智能优化器,否则效率明显低于单个树遍历。

    这种低效表达的例子

    //div/span[@class='b'] | //div[not(./span[@class='b'])]/span[@class='a'] 
    

    基于 XSLT 的验证

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/">
      <xsl:copy-of select=
      "//div/span
              [@class='b'
               or
                 @class='a'
                and
                 not(parent::*[span[@class='b']])
               ]"/>
     </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时

    <div class="info">
        <a href="/s/xyz.html" class="title">title</a>
        <span class="a">123</span>
        <span class="b">456</span>
        <span class="c">789</span>
    </div>
    

    对 Xpath 表达式求值,并将所选元素(在本例中只有一个)复制到输出

    <span class="b">456</span>
    

    当对不同的 XML 文档应用相同的转换时,其中没有 class='b'

    <div class="info">
        <a href="/s/xyz.html" class="title">title</a>
        <span class="a">123</span>
        <span class="x">456</span>
        <span class="c">789</span>
    </div>
    

    计算相同的 XPath 表达式并将正确选择的元素复制到输出

    <span class="a">123</span>
    

    【讨论】:

      【解决方案2】:

      xpath 表达式应该类似于:

      //div/span[@class='b'] | //div[not(./span[@class='b'])]/span[@class='a']
      

      联合运算符|左边的表达式会选择你所有div中的所有b类span,右边的表达式会先查询所有没有b类span的div然后选择他们的a级跨度。该|运算符将两个集合的结果结合起来。

      请参阅here 以使用 not() 选择节点,here 以将结果与 |运算符。

      另外,要参考您问题的第二部分,请查看here。 在 xpath 中使用 node() 您可以选择所选节点下方的所有内容(节点 + 文本)。所以你可以得到 ​​p> 返回的 div 中的所有内容

      //div/node()
      

      以供将来通过其他方式处理。

      【讨论】:

      • 谢谢,这很有启发性。我注意到not(span[@class='b']) 似乎像not(./span[@class='b']) 一样工作(前者是您提供的not() 链接的语法)。两者有区别吗?
      • 自从我上次使用 xpath 已经有一段时间了,所以为了安全起见,我从链接中复制了它。我认为它应该等效于 span[@class='b'] 必须是 div 的直接子级。另一方面,如果您使用 .//,您将在 DOM 中的 div 下方获得任何 span[@class='b'],即使它是孩子的​​孩子。但如果您想确定,请更详细地查看 w3school 链接中的 xpath 手册。
      【解决方案3】:

      在没有联合运算符的情况下对您的输入起作用的表达式:

      //div/span[@class='a' or @class='b'][count(../span[@class='b']) + 1]
      

      这只是为了好玩。我可能会在生产代码中使用更像@inVader 的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-19
        • 1970-01-01
        • 1970-01-01
        • 2011-02-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多