Published on

xpath入门文档

Authors
  • avatar
    Name
    Pony Ma
    Twitter

xpath入门文档

xpath 是什么

XPath(XML Path Language)全名 XML 路径语言,他是一种用来查询 XML 或 HTML 文档中指定位置数据的语言。

xpath 的选择功能十分强大,他提供了很多简单明了的路径选择表达式,还提供了 100 多个内建的函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。

我们做抓取任务时,完全可以使用 xpath 实现相应的信息抽取。

Tips: 我们通常说的 xpath 其实是 xpath 表达式

xpath 基础使用

示例 HTML 文本:

<!DOCTYPE html>
<html lang="en">
<body>
    <div>
        <ul>
            <li class="zhao">
                <a href="link1.html">link1</a>
                <span>title1</span>
            </li>
            <li class="zhao">
                <a href="link2.html">link2</a>
                <span>title2</span>
            </li>
            <li class="zhong">
                <a href="link3.html">link3</a>
                <span>title3</span>
            </li>
        </ul>
    </div>
</body>
</html>

我们需要先了解一下网页结构,了解一下节点的概念

网页结构

这是一个非常简单的 HTML 示例

<!DOCTYPE html>
<html lang="en">
<body>
    <div>
        <ul>
            <li class="zhao">
                <a href="link1.html">link1</a>
                <span>title1</span>
            </li>
            <li class="zhao">
                <a href="link2.html">link2</a>
                <span>title2</span>
            </li>
            <li class="zhong">
                <a href="link3.html">link3</a>
                <span>title3</span>
            </li>
        </ul>
    </div>
</body>
</html>

在 HTML 中,所有标签定义的内容都是节点,这些节点构成了一个 HTML 节点树,也叫做 HTML DOM 树,

  • 整个网站文档是一个文档节点
  • 每个html标签对应一个根节点
  • 每个节点内的文本是文本节点
  • 每个节点的属性是属性节点
  • 注释是注释节点

暂时无法在飞书文档外展示此内容

整个 HTML 文档就类似于一棵树,这种结构也被称为节点树,每一个分叉都是一个节点。

节点树中的节点彼此拥有层级关系,我们常用父(parent)、子(child)、兄弟(sibling)等来描述这些关系。父节点拥有子节点,同级的子节点被称为兄弟节点。

<a href="link1.html" id=1, class="link">link1</a>

href id class 都是a节点的属性

节点的属性也是一种节点,用于提供有关节点的额外信息或特性。您可以将节点属性看作是节点的附加描述,这些描述可以帮助您更好地理解和处理节点

所有节点

我们可以使用 //* 来选取所有的节点,也就是整个 HTML 文本中的所有节点,* 表示任意节点

可以通过指定节点名称来获取想要的节点,例如要获取所有的 li 节点,则是 //li

子节点

<li>
    <a>子链接</a>
</li>
<li>
    <a>子链接</a>
    <div>
        <a>孙链接</a>
    </div>
</li>

通过 / 或者// 来查找节点的子节点和子孙节点

例如要获取li 节点的所有直接的子节点a ,则是//li/a

如果要获取li 节点的所有的子孙节点a ,则是//li//a

要注意/// 的区别,/ 用于获取所有直接的子节点,// 用于获取所有的子孙节点

父节点

<li>
    <a>子链接</a>
</li>
<li>
    <a>子链接</a>
    <div>子div</div>
</li>

我们已经知道了如何获取子节点,假如我们知道了子节点,该怎样查找父节点呢? 可以通过.. 来实现

例如要获取所有a节点的父节点,则是//a/..

如果要获取所有 a 节点的父节点下的div 节点(即和a 同一级的div节点),则是//a/../div

拓展知识:也可以通过parent::* 来获取

文本获取

<li>
    <a>子链接</a>
</li>

当我们想要获取节点中的文本时,可以使用 text() 方法,

例如要获取所有a 节点中的文本,则是//a/text()

拓展知识:也可以通过string(.)来获取

属性获取

<li>
    <a href="/link.html">子链接</a>
</li>

我们可以用text() 来获取节点内部文本,那么节点属性该怎么获取呢?

我们可以通过通过@属性名 来获取,

例如要获取所有a 节点的href 属性,则是//a/@href

属性匹配

<li class="zhao">
    <a href="link1.html">link1</a>
</li>
<li class="zhao">
    <a href="link2.html">link2</a>
</li>
<li class="zhong">
    <a href="link3.html">link3</a>
</li>

如果想通过属性匹配来获取节点,则用中括号加属性名和值来限定某个属性

我们通过//li[@class="zhao"],限制了只获取class 属性为zhaoli 节点,符合条件的li 节点只有两个,所有结果也会只返回两个元素。

例如要获取所有href 属性值为link1.htmla 节点,则是//a[@href='link1.html']

属性多值匹配

有时候,某个节点的某个属性可能有多个值,例如

<li class="li li-first">
    <a href="link.html">first link</a>
 </li>
 <li class="li li-second">
    <a href="link.html">second link</a>
 </li>

这里的 HTML 文本中li 节点的class 属性就有两个值lili-first ,如果还用之前的属性匹配可能就无法匹配到所有的节点了

这种情况就需要用到contains 方法,则是//li[contains(@class, "li")]

contains 方法,第一个参数传入属性名称,第二个参数传入属性值,只要传入的节点的属性值里包含传入的属性,就可以成功匹配到。

常见的方法

刚刚我们提到了一个新的概念方法 ,xpath 的方法有很多,这里列举了一些比较常用的

方法名描述例子
contains包含//li[contains(@class, "li-first")] 获取li 节点的class 属性中包含li-first的节点
text获取文本//a/text() 获取a 节点中的文本
last获取最后一个节点的位置序号//li[last()] 获取最后一个li 节点
position获取当前节点的位置序号//li[position()<3] 获取位置序号小于 3 的节点
.....

多属性匹配

我们还有可能遇到另一种情况,就是根据多个属性确定一个节点,此时需要同时匹配多个属性,例如

<li class="li li-first" name="item">
    <a href="link.html">first link</a>
 </li>
 <li class="li li-second" name="item">
    <a href="link.html">second link</a>
 </li>
  <li class="li" name="next">
    <a href="next.html">next</a>
 </li>

这里的 HTML 文本中li 节点就含有多个属性

如果要获取正确的 li 节点,就需要有两个条件,class 属性里包含li ,同时name 属性值为item

我们需要用 xpath 的运算符and 来实现 ,即

//li[contains(@class, "li") and @name="item"]

常见的运算符

刚刚我们提到了一个新的概念运算符 ,xpath 的运算符有很多,这里列举了一些比较常用的

运算符描述例子
and并且、与//li[@class="li" and @name="item"]获取 class 为li 并且 name 为item 的li节点
or或者//li[@class="li" or @name="item"]获取 class 为li 或者 name 为item 的li节点
``计算两个节点的并集
>大于//li[position()>3] 获取第三个li节点之后的所有li 节点
<小于//li[position()<3] 获取第三个li节点之前的所有li 节点
......

按序选择

在选择节点时,可能会匹配到多个节点,但我们只想要其中的某一个,如第二个或者最后一个,这个时候该怎么处理呢?

<li class="item1"><a href="link.html">first link</a></li>
<li class="item2"><a href="link.html">second link</a></li>
<li class="item3"><a href="link.html">third link</a></li>
<li class="item4"><a href="link.html">fourth link</a></li>
<li class="item4"><a href="link.html">fifth link</a></li>

我们可以使用中括号中传入索引的方法获取特定次序的节点

选择第一个li ,即//li[1]

选择第三个li ,即//li[3]

选择最后一个li,即//li[last()]

选择前三个li,即//li[position()<=3]

节点轴选择

我们想通过节点的层级关系来获取节点时该怎么办呢

xpath 提供了很多节点轴的选择方法,包括获取子节点、兄弟节点、父节点、祖先节点等等

<div>
    <ul>
        <li class="item1"><a href="link.html">first link</a></li>
        <li class="item2"><a href="link.html">second link</a></li>
        <li class="item3"><a href="link.html">third link</a></li>
        <li class="item4"><a href="link.html">fourth link</a></li>
        <li class="item4"><a href="link.html">fifth link</a></li>
    </ul>
</div>

通过ancestor获取祖先节点,第一个li节点的所有祖先节点,例如//li[1]/ancestor::*

通过parent 获取父节点,例如第一个a 节点的父节点,//a/parent::*

通过child 获取子节点,例如第一个li 节点的子节点,//li/child::*

通过descendant 获取子孙节点,例如ul的子孙节点,//ul/descendant::*

通过following 获取当前节点之后的所有节点,例如li[@class="item1"]之后的所有节点,li[@class="item1"]/following::*

通过following-sibling 获取当前节点之后的所有同级节点,,例如li[@class="item1"]之后的所有节点,li[@class="item1"]/following-sibling::*

更多内容可查看 XPath 教程

如何选择合适的 xpath

使用唯一的特征

尽量选择具有唯一性的特征来定位节点,例如id 属性或者class 属性,保证你的 xpath 不会误选到其他的节点

<li id=1 class="item" style="color: black;"><a href="link.html">first link</a></li>
<li id=1 class="item" style="color: black;"><a href="link.html">second link</a></li>

//li[@class="item"]

//li[@style="color: black;"]

避免过于具体的 xpath

尽量编写通用的 XPath,以便在文档结构发生变化时,XPath 仍然有效。过于具体的 XPath 可能会使您的代码变得脆弱

<div>
    <ul>
        <li class="item" style="color: black;"><a href="link.html">first link</a></li>
    <ul>
</div>

//li[@class="item"]

/div/ul/li

使用属性特征

谓词是 XPath 表达式中的条件,它们允许您更精确地选择元素。例如,[@attribute='value']允许您根据元素的属性来选择

<li data-field="link"><a href="link.html">first link</a></li>
<li data-field="link"><a href="link.html">second link</a></li>
<li data-field="page"><a href="page.html">third link</a></li>

//li[@data-field="link"]

//li[position()<3]

xpath 练手实践

选择合适的下一页

寻找下一页按钮的 xpath 是我们经常遇到的场景,让我们通过几个例子来实践下上述所学的内容。

正常下一页

http://www.ygcgfw.com/gggs/001002/001002006/subpage-gggs.html

通过下边三种常用的 xpath 都可以成功获取到下一页,我们通常建议采用第二种

//ul[@class="m-pagination-page"]/li[7]/a

//ul[@class="m-pagination-page"]/li[last()-1]/a

//ul[@class="m-pagination-page"]/li/a[contains(text(), "下一页")]

第一页与后续页面不一致

http://ggzyjy-eweb.wenzhou.gov.cn/col/col1229678555/index.html###

遇到第一页的下一页 xpath 与后续页面的不一致该怎么办

通过下边两种常用的 xpath 都可以成功获取到,我们通常建议采用第一种

//div[@id="pagelist"]/a[last()-1]

//div[@id="pagelist"]/a[contains(text(), "下页")]

没有下一页

http://jyglj.sc.gov.cn/scjyglj/gjszfgk/list11646105227320.shtml

遇到没有下一页按钮的网站我们应该如何处理

//span[@class='pagination-first']/a[@class="pagination-num hover"]/following-sibling::*[1]