欢迎光临
我们一直在努力

Xpath

        XPath(XML Path Language)是一门在 XML/HTML 文档中查找和定位节点的语言。它最初用于 XSLT 和 XPointer,现在最广泛的应用是网页爬虫(如 Scrapy、lxml)和自动化测试(如 Selenium、Playwright)。

常见错点:

  • 索引从 1 开始,不是 0。

  • //div[1] 和 (//div)[1] 不同:

    • //div[1]:每个父节点下的第一个 div。

    • (//div)[1]:整个文档中第一个 div。

  • text() 只匹配直接文本子节点,不包含后代文本。要包含所有后代文本用 string(.) 或 .。

  • HTML 不区分大小写,XML 区分。浏览器解析 HTML 时标签名通常小写。

  • 一、XPath 的核心概念:节点树

    XPath 把 XML/HTML 文档看作一棵节点树,节点类型主要有:

    节点类型说明示例
    根节点 整棵树的根 /
    元素节点 HTML 标签 <div>、<a>
    属性节点 标签的属性 class="item"
    文本节点 标签内的文字 苹果
    注释节点 注释 <!– xxx –>
    命名空间节点 带命名空间的节点 ns:tag

    节点之间的关系:

    1. 父节点(Parent)

    定义:当前节点的直接上层节点。每个元素节点和属性节点都有且仅有一个父节点。根节点(/)没有父节点。

    示例:

    <book>
        <title>Python编程</title>
    </book>

    • <title> 的父节点是 <book>。

    • XPath 写法://title/parent::* 或 //title/.. 结果都是 <book>。

    2. 子节点(Child)

    定义:当前节点的直接下层节点。子节点可以是元素节点、文本节点、注释节点等,但不包含孙子节点。

    示例:

    <book>
        <title>Python编程</title>
        <author>张三</author>
    </book>

    • <book> 的子节点是 <title> 和 <author>。

    • XPath 写法:/book/child::title 等价于 /book/title。

    3. 兄弟节点(Sibling)

    定义:拥有同一个父节点的其他节点。分为两种:

    • 前置兄弟(preceding-sibling):当前节点之前的同级节点。

    • 后置兄弟(following-sibling):当前节点之后的同级节点。

    示例:

    <book>
        <title>Python编程</title>
        <author>张三</author>
        <price>59.9</price>
    </book>

    • <author> 的前置兄弟是 <title>,后置兄弟是 <price>。

    • XPath 写法:

      • 找前置兄弟://author/preceding-sibling::* → 结果 <title>

      • 找后置兄弟://author/following-sibling::* → 结果 <price>

    4. 祖先节点(Ancestor)

    定义:当前节点的所有上层节点,包括父节点、祖父节点,一直追溯到根节点。

    示例:

    <library>
        <shelf>
            <book>
                <title>Python编程</title>
            </book>
        </shelf>
    </library>

    • <title> 的祖先节点是 <book>、<shelf>、<library>(以及根节点)。

    • XPath 写法://title/ancestor::* → 结果包含 <book>、<shelf>、<library>。

    • 注意与 ancestor-or-self 的区别:后者会额外包含当前节点自身。

    5. 后代节点(Descendant)

    定义:当前节点的所有下层节点,包括子节点、孙节点,一直向下延伸到底。

    示例:

    <library>
        <shelf>
            <book>
                <title>Python编程</title>
            </book>
        </shelf>
    </library>

    • <library> 的后代节点是 <shelf>、<book>、<title>。

    • XPath 写法://library/descendant::title → 结果 <title>(等价于 //library//title)。

    • 注意与 descendant-or-self 的区别:后者会额外包含当前节点自身。

    补充:容易混淆的几组概念

    关系是否包含自身是否跨层级XPath 轴
    父节点 否 仅上一层 parent
    子节点 否 仅下一层 child
    兄弟节点 否 同级 preceding-sibling / following-sibling
    祖先节点 否 所有上层 ancestor
    后代节点 否 所有下层 descendant

    二、路径表达式基础

    1. 绝对路径 vs 相对路径

    表达式含义
    /html/body/div 从根节点开始的绝对路径
    //div 从任意位置匹配的相对路径(全文档搜索)
    ./div 当前节点下的 div
    ../div 父节点下的 div

    2. 常用符号

    符号含义
    / 子节点(或根)
    // 后代节点(任意层级)
    . 当前节点
    .. 父节点
    @ 属性
    * 通配符,匹配任意元素
    @* 任意属性
    text() 文本节点
    node() 任意节点

    示例

    /html/body/div          # body 下所有直接子 div
    //div                   # 文档中所有 div
    //div/p                 # 所有 div 的直接子 p
    //div//p                # 所有 div 的后代 p
    //div/*                 # div 下所有直接子元素
    //div[@class]           # 有 class 属性的 div
    //div[@class="item"]    # class 正好等于 item 的 div
    //a/@href               # 所有 a 的 href 属性值
    //p/text()              # 所有 p 的文本

    三、谓语(Predicates)

    谓语写在 [] 中,用来过滤节点。

    1. 按位置

    一)、基础位置谓语
    表达式含义
    [1] 第 1 个(索引从 1 开始)
    [2] 第 2 个
    [last()] 最后一个
    [last()-1] 倒数第 2 个
    [last()-2] 倒数第 3 个
    [position()=1] 等价于 [1]
    [position()=last()] 等价于 [last()]
    [position()<3] 前 2 个(位置 1、2)
    [position()<=3] 前 3 个
    [position()>2] 第 3 个及之后的所有
    [position()>=2] 第 2 个及之后的所有
    [position()!=2] 除了第 2 个以外的所有
    二)、按位置奇偶筛选
    表达式含义
    [position() mod 2 = 1] 奇数位置(第 1、3、5…个)
    [position() mod 2 = 0] 偶数位置(第 2、4、6…个)

    例子://ul/li[position() mod 2 = 0] → 选取所有偶数位置的 li

    三)、范围筛选(XPath 1.0 无 .. 范围写法)
    表达式含义
    [position()>=2 and position()<=5] 第 2 到第 5 个
    [position()>2 and position()<5] 第 3、4 个
    [position()<3 or position()>5] 前 2 个或第 6 个之后
    四)、按文本内容/属性间接定位位置

    位置谓语可以和其他条件组合,间接实现“按内容找位置”:

    表达式含义
    //li[text()="首页"] 文本为“首页”的 li(不管它是第几个)
    //li[contains(text(),"下一页")] 文本包含“下一页”的 li
    //tr[td[text()="张三"]] 包含“张三”的那一行 tr
    //div[@class="item"][3] class 为 item 的 div 中的第 3 个
    //div[@class="item" and position()<=3] class 为 item 且位置在前 3 的 div
    五)、嵌套位置(父子层级同时限制)
    表达式含义
    //ul[2]/li[1] 第 2 个 ul 下的第 1 个 li
    //ul/li[1] 每个 ul 下的第 1 个 li
    (//ul/li)[1] 整个文档中所有 ul/li 里的第 1 个
    //table/tr[2]/td[3] 第 2 行第 3 列
    //table/tr[last()]/td[last()] 最后一行最后一列

    2. 按属性

    一)、基础属性匹配
    表达式含义
    //input[@type] 拥有 type 属性的 input
    //input[@type="text"] type 属性值完全等于 text
    //input[@type!="text"] type 属性值不等于 text
    //input[not(@type)] 没有 type 属性的 input
    //input[@type and @name] 同时有 type 和 name
    //input[@type or @name] 有 type 或 name

    注意:[@type] 只判断“存在”,[@type="text"] 才判断具体值。

    二)、部分匹配(模糊匹配)
    表达式含义
    //div[contains(@class,"item")] class 属性包含 item
    //a[starts-with(@href,"http")] href 属性以 http 开头
    //a[ends-with(@href,".pdf")] href 属性以 .pdf 结尾
    //a[substring-after(@href,"://")="example.com"] :// 之后的部分等于 example.com
    //a[substring-before(@href,"?")="http://a.com"] ? 之前的部分等于该值
    三)、属性值的多条件组合
    表达式含义
    //input[@type="text" and @name="user"] 两个属性同时满足
    //input[@type="text" or @type="email"] 满足其一
    //input[@type="text" and not(@disabled)] 有 type 且无 disabled
    //input[@type="text"][@name="user"][@id="uid"] 多个谓语连写,等价于 and
    //div[contains(@class,"a") and contains(@class,"b")] class 同时含 a 和 b
    四)、按属性名(通配符)
    表达式含义
    //div[@*] 拥有任意属性的 div
    //div[not(@*)] 没有任何属性的 div
    //div[@*="x"] 任意属性的值等于 x
    //*[@id] 文档中所有带 id 属性的元素
    五)、按属性值去空白比较
    表达式含义
    //div[normalize-space(@class)="item"] 去首尾空白后比较
    //div[normalize-space(@title)="首页"] 同上
    //div[@class="item active"] 必须完全匹配整个字符串
    //div[contains(@class,"item")] 只要有 item 即可
    六)、大小写不敏感匹配属性值
    表达式含义
    //input[translate(@type,"TEXT","text")="text"] 大小写不敏感
    //a[translate(@href,"HTTP","http")="http://a.com"] 同上
    七)、属性值长度筛选
    表达式含义
    //input[string-length(@value)>5] value 属性长度大于 5
    //input[string-length(@value)=0] value 为空
    //input[string-length(normalize-space(@class))=4] 去空白后 class 长度恰好 4
    八)、属性值数字比较(属性值要转数字)
    表达式含义
    //div[@data-index="1"] 字符串比较
    //div[number(@data-index)>5] 转数字后比较
    //div[@data-index>5] XPath 会隐式转数字(部分环境可用)
    //div[number(@data-price)>=100] 价格大于等于 100

    示例:

    1. //input[@type="text"]

    含义:选取文档中所有 type 属性等于 "text" 的 input 元素。

    用途:在表单里定位所有文本框输入框。

    HTML 示例:

    <input type="text" name="username">
    <input type="password" name="pwd">
    <input type="text" name="email">

    结果:匹配第 1 个和第 3 个 input。

    2. //input[@type="text" and @name="user"]

    含义:选取同时满足两个条件的 input —— type="text" 并且 name="user"。

    用途:精确定位某个具体输入框,避免匹配到其他文本框。

    HTML 示例:

    <input type="text" name="user">
    <input type="text" name="email">

    结果:只匹配第 1 个。

    补充:and 是逻辑运算符,要求两边的条件都为真。

    3. //input[@type="text" or @type="email"]

    含义:选取 type="text" 或者 type="email" 的 input。

    用途:一次性匹配多种类型的输入框。

    HTML 示例:

    <input type="text" name="a">
    <input type="email" name="b">
    <input type="password" name="c">

    结果:匹配前两个。

    补充:or 只要一边为真即可。

    4. //a[@href]

    含义:选取所有带有 href 属性的 a 元素。

    用途:过滤掉没有链接地址的 <a>(例如仅作锚点或占位的 <a>)。

    HTML 示例:

    <a href="https://example.com">链接</a>
    <a name="top">锚点</a>
    <a>无属性</a>

    结果:只匹配第 1 个。

    补充:@href 只判断属性是否存在,不关心它的值是什么。

    5. //div[not(@class)]

    含义:选取没有 class 属性的 div 元素。

    用途:排除掉带样式的 div,常用于定位结构性的容器。

    HTML 示例:

    <div class="container">有 class</div>
    <div id="main">无 class</div>
    <div>无 class 无 id</div>

    结果:匹配第 2 个和第 3 个。

    补充:not() 是布尔函数,对条件取反。

    常见的属性筛选写法
    写法含义
    [@attr] 拥有该属性
    [@attr="值"] 属性等于某值
    [@attr!="值"] 属性不等于某值
    [not(@attr)] 没有该属性
    [@a="x" and @b="y"] 多条件同时满足
    [@a="x" or @b="y"] 满足任一条件
    [contains(@attr,"子串")] 属性值包含子串
    [starts-with(@attr,"前缀")] 属性值以某串开头

    3. 按文本

    一)、直接匹配文本节点
    1. 精确匹配
    表达式含义
    //button[text()="登录"] 文本完全等于“登录”
    //button[.="登录"] . 表示当前节点的字符串值,效果同上
    //button[string()="登录"] string() 转字符串后比较,等价
    2. 包含匹配
    表达式含义
    //button[contains(text(),"登")] 文本包含“登”
    //button[contains(.,"登")] . 写法,更常用
    //button[contains(string(),"登")] 显式转字符串
    3. 开头/结尾匹配
    表达式含义
    //p[starts-with(text(),"Hello")] 文本以 Hello 开头
    //p[starts-with(.,"Hello")] 同上
    //p[ends-with(.,"world")] 仅 XPath 2.0+,1.0 不支持
    //p[substring(., string-length(.)-4) = "world"] 取最后 5 个字符比较
    二)、去除空白后再匹配

    HTML 文本经常带空格、换行,直接 text()="登录" 可能匹配不到。

    表达式含义
    //button[normalize-space(text())="登录"] 去首尾空白并合并中间空白后比较
    //button[normalize-space(.)="登录"] 同上
    //button[normalize-space()="登录"] 省略参数默认对当前节点

    例子:

    <button>  登录  </button>

    • //button[text()="登录"] →  匹配不到

    • //button[normalize-space(text())="登录"] →  匹配到

    三)、按文本长度筛选
    表达式含义
    //p[string-length(text())>10] 文本长度大于 10
    //p[string-length(normalize-space(.))=4] 去空白后长度恰好为 4
    //p[string-length(text())<3] 文本长度小于 3

    例子:

            //a[string-length(normalize-space(.))=2] → 选取文本恰好 2 个字的 a。

    四)、按文本包含关键词的多种组合
    表达式含义
    //div[contains(.,"Python") and contains(.,"爬虫")] 文本同时包含 Python 和 爬虫
    //div[contains(.,"Python") or contains(.,"Java")] 包含 Python 或 Java
    //div[not(contains(.,"广告"))] 不包含“广告”
    //div[contains(text(),"错误") and not(contains(text(),"警告"))] 组合条件
    五)、对“后代文本”进行匹配

    text() 只匹配直接文本子节点,不包含后代元素的文本。要包含所有后代文本,用 . 或 string(.)。

    写法匹配范围
    //div[text()="Hello"] 只匹配 div 的直接文本节点恰好是 Hello
    //div[.="Hello"] 匹配 div 及其所有后代的合并文本值等于 Hello
    //div[contains(.,"Hello")] 匹配 div 及其后代文本中包含 Hello

    例子:

    <div>Hello <span>World</span></div>

    • //div[text()="Hello"] →  匹配(直接文本是 "Hello")

    • //div[text()="Hello World"] →  不匹配(text() 只看直接文本)

    • //div[.="Hello World"] →  匹配(. 合并了后代文本)

    • //div[contains(.,"World")] →  匹配

    六)、用 translate() 实现大小写不敏感匹配

    XPath 1.0 没有 lower-case(),可用 translate() 替代。

    表达式含义
    //p[translate(text(),"ABC","abc")="hello"] 把大写字母转小写后比较
    //p[translate(.,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")="hello"] 完整大小写不敏感匹配

    例子:

    <p>Hello</p>

    • //p[text()="hello"] → 大小写不匹配

    • //p[translate(text(),"HELLO","hello")="hello"] → 匹配

    七)、按文本选出的节点再做位置筛选
    表达式含义
    //li[text()="首页"] 文本为“首页”的 li
    (//li[contains(text(),"页")])[1] 包含“页”的 li 中的第 1 个
    //li[contains(.,"页")][last()] 包含“页”的 li 中的最后一个
    //tr[td[text()="张三"]]/td[2] 找到含“张三”的行,取第 2 列
    八)、按子元素的文本筛选父节点
    表达式含义
    //div[span[text()="价格"]] 包含文本为“价格”的 span 的 div
    //tr[td[contains(.,"总价")]] 包含“总价”单元格的行
    //ul[li[text()="首页"]] 包含文本为“首页”的 li 的 ul
    //div[p[contains(text(),"错误")]] 包含含“错误”文本的 p 的 div
    九)、按文本筛选属性值(不是元素文本)

    如果文本在属性里,要用 @属性名 而不是 text()。

    表达式含义
    //a[@title="首页"] title 属性等于“首页”
    //a[contains(@title,"首页")] title 属性包含“首页”
    //a[starts-with(@title,"首")] title 属性以“首”开头
    //input[@value="提交"] value 属性等于“提交”
    //a[normalize-space(@title)="首页"] 属性去空白后比较
    十)、按文本模糊匹配正则(XPath 2.0+ / 3.0)
    表达式含义
    //p[matches(.,"^Hello.*world$")] 正则匹配
    //p[matches(.,"\\d{4}-\\d{2}-\\d{2}")] 匹配日期格式
    //p[replace(.,"\\s+","")="Hello"] 正则替换后再比较

    4. 按子元素

    一)、基础:判断是否包含某子元素
    表达式含义
    //div[p] 包含 p 子元素的 div
    //div[not(p)] 不包含 p 子元素的 div

    注意:[p] 只看直接子元素,不看后代。要包含后代用 [.//p]。

    二)、同时包含多个子元素
    表达式含义
    //div[p and span] 同时包含 p 和 span 子元素
    //div[p or span] 包含 p 或 span
    //div[p][span] 多谓语连写,等价于 and
    //div[p and not(span)] 包含 p 但不包含 span
    //div[p and span and a] 同时包含三种子元素
    三)、按子元素数量筛选
    表达式含义
    //div[count(p)=1] 恰好有 1 个 p 子元素
    //div[count(p)>2] 多于 2 个 p 子元素
    //div[count(p)>=2] 至少 2 个
    //div[count(p)<3] 少于 3 个
    //div[count(*)=0] 没有任何子元素
    //div[count(*)=1] 恰好 1 个子元素
    //ul[count(li)>5] li 数量大于 5 的 ul
    四)、按子元素文本筛选
    表达式含义
    //div[p[text()="价格"]] 包含文本为“价格”的 p 的 div
    //div[p[contains(.,"错误")]] 包含文本含“错误”的 p 的 div
    //tr[td[text()="张三"]] 包含“张三”单元格的行
    //ul[li[contains(text(),"首页")]] 包含“首页”li 的 ul
    //div[span[normalize-space(.)="确定"]] 去空白后文本为“确定”的 span 的父 div
    五)、按子元素属性筛选
    表达式含义
    //div[a[@href]] 包含有 href 的 a 的 div
    //div[input[@type="text"]] 包含 type=text 输入框的 div
    //div[a[contains(@href,"/list")]] 包含 href 含 /list 的 a 的 div
    //form[input[@name="user"] and input[@name="pwd"]] 同时包含用户名和密码输入框的表单
    //div[span[@class="price"]] 包含 class=price 的 span 的 div
    六)、按子元素的子元素(多层级)
    表达式含义
    //div[ul/li] 包含 ul,且 ul 下含 li 的 div
    //div[ul/li[text()="首页"]] 包含“首页”li 的 div
    //table[tr/td[text()="总价"]] 包含“总价”单元格的表格
    //div[section/article/h2] 多层嵌套路径作为条件

    用路径当谓语时,ul/li 是相对于 div 的直接子级路径。

    七)、后代 vs 直接子元素
    表达式含义
    //div[p] 只有直接子元素 p 才匹配
    //div[.//p] 任意后代中有 p 就匹配
    //div[descendant::p] 等价于 .//p
    //div[child::p] 等价于 [p]

    例子:

    <div><section><p>Hello</p></section></div>

    • //div[p] →  不匹配(p 不是直接子元素)

    • //div[.//p] →  匹配(p 是后代)

    • //div[descendant::p] →  匹配

    八)、按子元素位置筛选
    表达式含义
    //div[p[1]] 第一个子元素是 p 的 div(等价 //div[p and position()…],注意写法)
    //div[*[1][self::p]] 第一个子元素恰为 p
    //div[last()[self::p]] 最后一个子元素是 p
    //div[count(p)=2 and count(span)=1] 组合数量条件

    //div[p[1]] 的准确含义是:div 含 p 子元素,且该 p 是它父节点下的第 1 个 p。要表达“第一个子元素是 p”,用 //div[*[1][self::p]]。

    九)、子元素为空 / 非空
    表达式含义
    //div[not(*)] 没有任何子元素(空元素)
    //div[not(node())] 没有任何子节点(包括文本)
    //div[not(text())] 没有直接文本
    //div[text()] 有直接文本
    //td[not(*)] 空单元格

    四、轴(Axes)——按节点关系定位

    轴用于指定相对于当前节点的方向。语法:轴::节点测试[谓语]

    轴含义
    child 子节点(默认轴)
    parent 父节点
    descendant 所有后代
    ancestor 所有祖先
    descendant-or-self 后代及自身
    ancestor-or-self 祖先及自身
    following-sibling 之后的所有兄弟
    preceding-sibling 之前的所有兄弟
    following 文档中之后的所有节点
    preceding 文档中之前的所有节点
    self 自身
    attribute 属性
    namespace 命名空间

    1. child

    精确含义:当前节点的直接子元素(不包括文本节点、注释节点、属性)。

    例子:

    //div/child::p        # div 的直接子 p
    //div/p               # 等价写法(child 是默认轴,可省略)

    child:: 只包含元素,不包含文本。想选文本用 child::text()。

    2. parent(父节点)

    精确含义:当前节点的直接父节点,每个节点最多只有一个。

    简写:..

    例子:

    //p/parent::div        # p 的直接父 div
    //p/..                 # 等价写法

    根节点的 parent 是空节点集,不会报错,但匹配不到东西。

    3. descendant(所有后代)

    精确含义:当前节点的所有层级的子节点、孙节点、曾孙节点……(不包含自己)。

    简写://(如 //book//title)

    例子:

    //div/descendant::p    # div 里所有层级的 p(不管嵌套多深)
    //div//p               # 等价写法

    descendant:: 不包含自己;包含自己要用 descendant-or-self::。

    4. ancestor(所有祖先)

    含义:当前节点的所有上级节点,一直到根节点(不包含自己)。

    例子:

    //span/ancestor::div   # span 的所有 div 祖先(可能多个)
    //span/ancestor::*     # span 的所有祖先元素

    5. descendant-or-self(后代及自身)

    精确含义:descendant + self 的并集,包含自己和所有后代。

    例子:

    //div/descendant-or-self::* # div 自己 + 所有后代元素
    //div/descendant-or-self::p # div 自己 + 后代 p

    6. ancestor-or-self(祖先及自身)

    精确含义:ancestor + self 的并集,包含自己和所有祖先。

    例子:

    //span/ancestor-or-self::*     # span 自己 + 所有祖先
    //div/ancestor-or-self::div    # div 自己 + 所有 div 祖先

    7. following-sibling(之后的所有兄弟)

    精确含义:当前节点之后的同级兄弟节点(同一个父节点下的后续兄弟元素),不包含自己,不包含各自的后代。

    例子:

    //label/following-sibling::input   # label 后面的所有 input 兄弟
    //li[1]/following-sibling::li      # 第 1 个 li 后面的所有 li

    只算同级,不算子级

    8. preceding-sibling(之前的所有兄弟)

    精确含义:当前节点之前的同级兄弟节点,不包含自己,不包含各自的后代。

    例子:

    //input/preceding-sibling::label   # input 前面的所有 label
    //li[last()]/preceding-sibling::li # 最后一个 li 前面的所有 li

    9. following(文档中之后的所有节点)

    精确含义:当前节点结束标记之后的所有节点(不限于同级,包含其他分支),不包含自己节点的后代、不包含自己。

    例子:

    //h2/following::p[1]        # h2 之后文档中的第 1 个 p(不限同级)

    10. preceding(文档中之前的所有节点)

    精确含义:当前节点开始标记之前的所有节点(不限于同级),不包含祖先、不包含自己及其自己的后代。

    例子:

    //h2/preceding::p[1]        # h2 之前文档中的第 1 个 p

    11. self(自身)

    精确含义:当前节点本身。

    简写:.(点)

    例子:

    //div/self::div            # 匹配 div 自己
    //div/self::*              # 等价于 //div
    //p[self::p]               # 常用于谓语中判断自己是 p

    单独用没意义,主要用于 ancestor-or-self、descendant-or-self 或在谓语中判断。

    12. attribute(属性)

    精确含义:当前节点的所有属性节点(不是元素)。

    简写:@

    例子:

    //div/attribute::class     # 等价于 //div/@class
    //div/attribute::*         # div 的所有属性

    13. namespace(命名空间)

    精确含义:当前节点的所有命名空间节点(极少用)。

    总示例:

    <?xml version="1.0" encoding="UTF-8"?>
    <catalog>
    <book id="b1" lang="en">
    <title>XPath Guide</title>
    <author>Alice</author>
    <price>30</price>
    </book>
    <book id="b2" lang="zh">
    <title>Python爬虫</title>
    <author>Bob</author>
    <price>50</price>
    </book>
    <magazine id="m1">
    <title>Tech Weekly</title>
    </magazine>
    </catalog>

    接下来,以 <book id="b1"> 为当前节点,依次讲解 13 个轴。

    //book[@id="b1"]/child::title # 匹配 <title>XPath Guide</title>,在显示节点时,会默认显示这个元素节点内部的文本内容,这是展示效果,不是选中了文本节点。

    //book[@id="b1"]/parent::* # 匹配 <catalog>

    //book[@id="b1"]/descendant::* # book(b1) 内所有后代元素<title> <author><price>

    //title/ancestor::* # title 的所有祖先(book、catalog)

    //book[@id="b1"]/descendant-or-self::* # book(b1) 自己 + 该节点下的所有后代

    //book[@id="b1"]/ancestor-or-self::* # book(b1) 自己 + 所有祖先

    //book[@id="b1"]/following-sibling::* # b1 后面的所有兄弟:<book(b2)> <magazine>

    //book[@id="b2"]/preceding-sibling::* # b2 前面的兄弟 <book(b1)>

    //book[@id="b1"]/following::* # b1 结束标记之后的所有节点: <book(b2)>及其子节点、<magazine>及其子节点

    //magazine/preceding::* # magazine 之前的所有节点

    //book[@id="b1"]/self::* # 匹配 book b1 自己

    五、函数大全

    XPath 函数分几大类,以下是最常用的。

    1. 字符串函数

    函数说明示例
    contains(s, sub) s 是否包含 sub contains(text(),"abc")
    starts-with(s, pre) s 是否以 pre 开头 starts-with(@href,"http")
    ends-with(s, suf) 是否以 suf 结尾(XPath 2.0+) ends-with(@href,".pdf")
    string-length(s) 长度 string-length(text())>5
    normalize-space(s) 去首尾空白并合并中间空白 normalize-space(text())="Hi"
    substring(s, start, len) 截取 substring(text(),1,3)
    substring-before(s, sub) sub 之前的部分 substring-before(@href,"?")
    substring-after(s, sub) sub 之后的部分 substring-after(@href,"?")
    translate(s, from, to) 字符替换 translate(text(),"ABC","abc")
    concat(s1, s2, …) 拼接 concat(@first," ",@last)
    string(s) 转字符串 string(//div)
    upper-case(s) 转大写(2.0+)
    lower-case(s) 转小写(2.0+)

    2. 布尔函数

    函数说明
    not(expr) 取反
    true() / false() 布尔值
    boolean(expr) 转布尔

    3. 数值函数

    函数说明
    count(nodes) 节点数量
    sum(nodes) 求和
    floor(n) / ceiling(n) 向下/向上取整
    round(n) 四舍五入
    number(s) 转数字

    4. 节点函数

    函数说明
    last() 最后一个位置
    position() 当前位置
    name() 节点名
    local-name() 不带前缀的节点名
    text() 文本节点

    5. 逻辑运算符

    运算符描述示例说明
    and 逻辑与(同时满足) //input[@type="text" and @name="user"] 选取 type 为 text 且 name 为 user 的 input
    or 逻辑或(满足其一) //div[@class="a" or @class="b"] 选取 class 为 a 或 b 的 div
    not() 逻辑非(取反) //input[not(@disabled)] 选取没有 disabled 属性的 input

    注意事项

    • and 的优先级高于 or,混用时建议用括号明确逻辑,例如 //div[(a or b) and c]。

    • not() 是函数,不是运算符,需写 not(expr)。

    • 多个谓语连写 [@a][@b] 等价于 and,但可读性不同。

    用法示例

    # 多条件组合:同时满足多个属性
    //input[@type="text" and @name="user" and not(@disabled)]

    # 范围组合:价格大于 20 且小于 50
    //book[price>20 and price<50]

    # 文本与属性组合:文本包含"登录"且 class 含 btn
    //button[contains(text(),"登录") and contains(@class,"btn")]

    # 排除特定内容:包含"错误"但不含"警告"
    //div[contains(.,"错误") and not(contains(.,"警告"))]

    # 多选一:type 是 text 或 email
    //input[@type="text" or @type="email"]

    6. 比较运算符

    运算符描述示例说明
    = 等于 //div[@id="main"] 选取 id 为 main 的 div
    != 不等于 //div[@id!="main"] 选取 id 不为 main 的 div
    < 小于 //price[. < 100] 选取价格小于 100 的 price
    <= 小于等于 //price[. <= 100] 价格小于等于 100
    > 大于 //price[. > 100] 价格大于 100
    >= 大于等于 //price[. >= 100] 价格大于等于 100

    注意事项

    • 比较运算符中的 < 和 > 在 XML 文档中需要转义为 &lt; 和 &gt;,但在 XPath 表达式中通常直接写即可(取决于宿主语言,如 Python 的 lxml 可直接写)。

    • 字符串比较是区分大小写的,"Main" 和 "main" 不相等。

    • 属性值默认是字符串,做数值比较时建议用 number(@attr) 显式转换,例如 //div[number(@data-index)>5]。

    • != 在节点集比较中有特殊语义:只要有一个节点值不等于目标值就为真,而非“所有节点都不等于”。需谨慎使用。

    用法示例

    # 数值比较(注意:属性值需显式转数字)
    //div[number(@data-price) > 100]

    # 字符串比较
    //a[@href="https://example.com"]

    # 与逻辑运算符组合
    //book[price>=20 and price<=50]

    # 不等式的实际场景:排除隐藏元素
    //div[@style!="display:none"]

    # 节点集比较(存在性判断)
    //div[p > 10]    # 存在一个文本值大于 10 的 p 子元素

    六、 Xpath 运算符

    1、并集运算符 |

    运算符描述示例
    | 计算两个节点集的并集 //title | //author

    补充说明:

    • 并集只能用于节点集,不能用于字符串或数字。

    • 它会去除重复节点(同一个节点在两个集合中只出现一次)。

    • 示例://book/title \\| //book/author → 返回所有 title 和 author。

    2、算术运算符

    运算符描述示例
    + 加法 //book[price=10+5]
    – 减法 //book[price=20-5]
    * 乘法 //book[price=5*4]
    div 除法 //book[price=50 div 2]
    mod 取余 //book[price=10 mod 3]

    补充说明:

    • XPath 中除法用 div 而非 /(/ 是路径分隔符)。

    • 取余用 mod,例如 5 mod 2 = 1。

    • 算术结果可用于谓语中的比较,例如 //book[price=10+5] 等价于 //book[price=15]。

    • 更实用的场景://div[position() mod 2 = 1] 选取奇数位置。

    • 字符串转数字:如果属性值是字符串,做算术时最好用 number() 显式转换,例如 //book[number(@price) > 20]。

    示例:

    //book[price=10+5]           # 价格等于 15
    //book[price=50 div 2]       # 价格等于 25
    //book[price=10 mod 3]       # 价格等于 1
    //li[position() mod 2 = 0]   # 偶数位置的 li
    //div[number(@data-index) * 2 = 10]  # data-index 为 5

    3、比较运算符

    运算符描述示例
    = 等于 //book[@id="b1"]
    != 不等于 //book[@id!="b1"]
    < 小于 //book[price<30]
    <= 小于等于 //book[price<=30]
    > 大于 //book[price>30]
    >= 大于等于 //book[price>=30]

    补充说明:

    • 字符串比较区分大小写,"Main" 和 "main" 不同。

    • 属性值默认是字符串,做数值比较时建议用 number(@attr),例如 //div[number(@data-index)>5]。

    • != 在节点集比较中语义特殊:只要存在一个节点值不等于目标值,整个条件就为真。

    • 不等于的坑://book[@id!="b1"] 会排除 id 为 b1 的节点,但如果节点没有 id 属性,也会被排除(因为没有 id 属性无法判定“不等于”)。

    示例:

    //price[. > 100]                     # 价格大于 100
    //price[. <= 100]                    # 价格小于等于 100
    //div[number(@data-price) >= 50]     # 数值比较,显式转数字
    //a[@href!="/home"]                  # 链接不是 /home 的 a
    //book[price>=20 and price<=50]      # 范围比较

    4、逻辑运算符

    运算符描述示例
    and 逻辑与(同时满足) //book[price>20 and price<50]
    or 逻辑或(满足其一) //book[price>20 or price<10]
    not() 逻辑非(取反,是函数) //input[not(@disabled)]

    补充说明:

    • not() 是函数,不是运算符,写法为 not(expr)。

    • and 的优先级高于 or,混用时建议加括号,例如 //div[(a or b) and c]。

    • 多谓语连写 [@a][@b] 等价于 and。

    示例:

    //input[@type="text" and @name="user"]
    //div[@class="a" or @class="b"]
    //input[not(@disabled)]
    //book[price>20 and price<50]
    //div[contains(.,"错误") and not(contains(.,"警告"))]

    5. 路径运算符

    符号描述示例
    / 子节点 /library/book
    // 后代节点 //book
    . 当前节点 ./title
    .. 父节点 ../book
    @ 属性 @id
    * 通配符(任意元素) //*
    @* 任意属性 //div[@*]
    :: 轴分隔符 child::book

    注意:* 在算术里是乘法,在路径里是通配符,取决于上下文。


    字符串与节点比较

    写法含义
    //book[title="Python"] 子元素 title 的文本等于 Python
    //book[@id="b1"] 属性 id 等于 b1
    //book[.="Hello"] 当前节点字符串值等于 Hello(含后代文本)
    //book[text()="Hello"] 直接文本节点等于 Hello

    节点集与数值比较的特殊规则

    • 当节点集与数值比较时,只要任一节点满足条件即为真。例如 //book[price>20] 中,price 是节点集,只要有一个 price 大于 20 就匹配。

    XPath 运算符优先级(从高到低)

    优先级运算符
    1 ()(括号)
    2 [](谓语)
    3 / //(路径)
    4 |(并集)
    5 + -(加减)
    6 * div mod(乘除余)
    7 = != < <= > >=(比较)
    8 and
    9 or
    赞(0)
    未经允许不得转载:171主机测评 » Xpath
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址