XPath 参考手册

XPath简介

网络上许多文档都是结构化的,最常见的HTML文档就是典型的结构化文档,结构化文档因其自身内容组织具有良好的结构特征,使得这类文档可以灵活的用于数据存储,而XPath正是用于高效访问like-XML文档的语言,XPath语言虽然简单,但却可以编写出非常复杂的文档访问逻辑。

XPath工作流程:从一个初始节点集开始,然后通过条件测试来筛选新的节点集,不断如此,最后得到结果的节点集。

目录

[TOC]

轴体现了节点跟文档中其它节点的关系,比如节点间的父子关系、兄弟关系、祖先关系等,所以在对节点集进行条件测试之前,可以先用轴来从节点集中进行筛选。默认的轴是child::,也即语法中忽略轴信息时,就认为筛选出当前节点集的子节点集以进行后续的条件测试。

样例

XPath:/foo/following-sibling::bar

  1. 以文档根节点作为初始节点集(仅含根节点)
  2. /foo表示遍历当前节点集,将节点的子节点中名称为foo的节点作为新节点集的元素
  3. /following-sibling::bar表示遍历当前节点集,将节点的后续兄弟节点中名称为bar的节点作为新节点集的元素

常用轴

  1. 祖先轴,当前节点的祖先节点

    ancestor::element-name

  2. 祖先轴+当前节点

    ancestor-or-self::element-name

  3. 属性轴,当前节点的属性

    attribute::attr-name

  4. 孩子轴,当前节点的儿子节点

    child::element-name

  5. 后代轴,当前节点的后代节点

    descendant::element-name

  6. 后代轴+当前节点

    descendant-or-self::element-name

  7. 节点树中位于当前节点之后的所有节点,不包括后代节点、属性节点、名称空间节点

    following::element-name

  8. 节点树中位于当前节点之后的所有兄弟节点

    following-sibling::element-name

  9. 名称空间轴

    namespace::

  10. 父亲轴,当前节点的父节点

parent::element-name

  1. 节点树中位于当前节点之前的所有节点,不包括祖先节点、属性节点、名称空间节点

    preceding::element-name

  2. 节点树中位于当前节点之前的所有兄弟节点

    preceding-sibling::element-name

  3. 指向当前节点自身的轴

    self::

条件测试

条件测试主要分为step和predicate两个动作。step就是进入下一层级元素以获得新节点;predicate则是对当前节点进行测试,保留通过测试的,丢掉测试失败的。:predicate中又包含step和predicate

样例

XPath:/foo/bar

  1. 以文档根节点作为初始节点集(仅含根节点)
  2. /foo表示遍历当前节点集,将节点的子节点中名称为foo的节点作为新节点集的元素
  3. /bar表示遍历当前节点集,将节点的子节点中名称为bar的节点作为新节点集的元素

XPath:/foo[bar]

  1. 以文档根节点作为初始节点集(仅含根节点)
  2. /foo表示遍历当前节点集,将节点的子节点中名称为foo的节点作为新节点集的元素
  3. [bar] 表示遍历当前节点集,将含有bar子节点的节点作为新节点集的元素,不含有bar子节点的节点则被丢弃

XPath的Shortcut

  1. child 是默认轴,如果意图运用测试来筛选节点集的子节点,则可以在条件测试的前面忽略child::
  2. 属性轴的简写,attribute::href 可以简写为@href
  3. 后代轴的简写,/descendant-or-self::foo可以简写为//foo
  4. 位置筛选简写,[position() = 1]可以简写为[1]
  5. 当前节点.
  6. 父节点..

常用实例

选取两个节点之间的所有邻居节点

样例文档

<div>
	<h3>标题一</h3>
	<p>one</p>
	<p>two</p>
	<h3>标题二</h3>
    <p>three</p>
    <p>four</p>
    <h3>标题三</h3>
    <p>five</p>
    <h3>无用标题</h3>
</div>

网页中经常会遇到列表类型的内容,列表又往往会包含多个子列表,如上面样例所示 <div></div> 代表整个列表,而其中的每个 <h3></h3> 则代表子列表的开始部分,下面将详细讲解如何分别提取各个子列表。

如果子列表数目是固定的,可以直接使用如下语法来提取子列表:

# 提取第一个子列表
//p[count(preceding-sibling::h3)=1]
# 提取第二个子列表
//p[count(preceding-sibling::h3)=2]
# 依次类推可以提取第N个子列表

不过网页中子列表的数目往往是未知的、不固定的,因此需要借助其它方法来提取子列表。在介绍解决方案之前先来看一个叫做 Kayessian 方法的公式:

$ns1[count(.|$ns2) = count($ns2)]

其中 $ns1$ns2 分别表示节点集,上述公式得到的结果是这两个节点集的交集。 Kayessian 方法是 XPath 1.0 求节点集交集的方法,如果使用的是 XPath 2.0 ,可以直接使用 $ns1 intersect $ns2 来求交集。

这里我们将 Kayessian 方法应用于获取两个节点之间所有的邻居节点,现在假设我们要提取第二个子列表(即“标题二”到“标题三”之间的内容),取 $ns1//h3[2]/following-sibling::node()$ns2//h3[3]/preceding-sibling::node(),替换 Kayessian 公式中的 $ns1$ns2 后将得到:

//h3[2]/following-sibling::node()[
  count(.|//h3[3]/preceding-sibling::node())
  =
  count(//h3[3]/preceding-sibling::node())
]

只要更改其中 h3[$i] 的位置参数 $i 就可以提取任意子列表。

语法参考

样例文档

<root xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org">
    <actors>
        <actor id="1">Christian Bale</actor>
        <actor id="2">Liam Neeson</actor>
        <actor id="3">Michael Caine</actor>
    </actors>
    <foo:singers>
        <foo:singer id="4">Tom Waits</foo:singer>
        <foo:singer id="5">B.B. King</foo:singer>
        <foo:singer id="6">Ray Charles</foo:singer>
    </foo:singers>
    <description>
        <profile refid="1">Christian Bale is a man</profile>
        <profile refid="2">Liam Neeson is a man</profile>
        <profile refid="3">Michael Caine is a woman</profile>
    </decription>
	
    <bookstore specialty="novel">
      <book style="autobiography">
        <author>
          <username>Joe Bob</username>
          <award>Trenton Literary Review Honorable Mention</award>
        </author>
        <price>12</price>
      </book>
      <book style="textbook">
        <author>
          <username>Mary Bob</username>
          <publication>Turing</publication>
        </author>
        <editor>
          <username>Britney Bob</username>
        </editor>
        <price>55</price>
      </book>
      <book style="novel" id="myfave">
        <author>
          <username>Toni Bob</username>
          <degree from="Trenton U">B.A.</degree>
          <degree from="Harvard">Ph.D.</degree>
          <award>Pulitzer</award>
          <publication>Still in Trenton</publication>
          <publication>Trenton Forever</publication>
        </author>
        <price intl="Canada" exchange="0.7">6.50</price>
        <excerpt>
          <p>It was a dark and stormy night.</p>
        </excerpt>
      </book>
    </bookstore>
</root>

语法

  1. 文档节点

    /

  2. 文档最高层次节点root

    /root

  3. 文档中root节点的子节点actors的所有actor儿子节点

    /root/actors/actor

  4. 文档中在名称空间foo下的singer节点且不考虑其在文档中的位置

    //foo:singer

  5. 节点root下的所有后代actor节点

    /root//actor

  6. 文档中所有的foo:singer节点的id属性值

    //foo:singer/@id

  7. 文档中从前往后第一个actor节点的文本内容

    //actor[1]/text()

  8. 文档中从前往后最后一个actor和倒数第二个actor

    //actor[last()] //actor[last() - 1]

  9. 文档中每个actors的最后一个actor子节点

    //actors/actor[last()]

  10. 文档中所有actors的所有actor子节点的最后一个

//(actors/actor)[last()]

  1. 文档中从前往后第一个和第二个actor节点

    //actor[position() < 3]

  2. 文档中含有属性id的所有actor节点

    //actor[@id]

  3. 文档中属性id值等于3的所有actor节点

    //actor[@id=‘3’]

  4. 文档中属性id值小于等于3的所有actor节点

    //actor[@id<=3]

  5. 文档中foo:singers节点的所有儿子节点

    /root/foo:singers/*

  6. 文档中root所有的孙子节点actor

    /root/*/actor

  7. 文档中含有属性id的所有节点

    //*[@id]

  8. 文档中的所有节点

    //*

  9. 文档中所有actorfoo:singer节点

    //actor|//foo:singer

  10. 文档中第一个元素的名称

    name(//*[1])

  11. 文档第一个actor节点的属性id的数值表示

    number(//actor[1]/@id)

  12. 文档第一个actor节点的属性id的字符串表示

    string(//actor[1]/@id)

  13. 文档第一个actor节点的内容长度

    string-length(//actor[1]/text())

  14. 文档中第一个foo:singer的名称(不含名称空间)

    local-name(//foo:singer[1])

  15. 文档中foo:singer节点的数目

    count(//foo:singer)

  16. 文档中foo:singer节点的所有属性

    //foo:singer/@*

  17. 名称空间foo下的所有属性

    @foo:*

  18. 文档中的跟第一个actor相关的profile节点

    //profile[@refid=//actor[1]/@id]

  19. 文档中含有actor的第一个actors节点

    //actors[actor][1]

  20. 从当前节点上下文中获取子节点actor

    actor ./actor

  21. 对文档中所有foo:singer节点的id属性值求和

    sum(//foo:singer/@id)

  22. 文档中含有author/degreebook节点

    book[author/degree]

  23. 文档中所有含有degreeeaward子节点的author节点

    author[degree][award] author[degree and award]

  24. 文档中含有子节点degreeaward且同时含有publicationauthor节点

    author[(degree or award) and publication]

  25. 文档中含有子节点degree且不含有publicationauthor节点

    author[degree and not(publication)]

  26. 文档中含有值等于Bob的子节点username的节点author

    author[username=“Bob”]

  27. 文档中所有值不等于Bob的节点author

        author[. != "Bob"]
    
  28. 文档中含有值等于Bobusername子节点的author节点,且其兄弟节点price大于50

        author[username="Bob" and ../price > 50]
    
  29. 文档中含有任意子节点值等于Bobauthor节点

        author[*="Bob"]
    
  30. 节点p的第二个文本节点内容

        p/text()[2]
    
  31. 当前节点最近的book祖先节点

        ancestor::book[1]
    
  32. 当前节点最近的author祖先节点,且其父亲节点是book

        ancestor::author[parent::book][1]
    
  33. 当前节点的所有儿子节点(不含文本节点)

        /*
    
  34. 当前节点的所有儿子节点(含有文本节点)

        /child::node()
    
  35. 当前节点所有不含文本的儿子节点

        /child::node()[not(text())]
    
  36. 当前节点的所有儿子节点(不含文本节点)

        /child::node()[not(self::text())]
Xiao Wenbin
Xiao Wenbin
Natural Language Processing Engineer

My research interests include machine learning, information retrieval and natural language processing.

Related