XPath 语法速查表 - XML/HTML 选择器语法大全

从 XML/HTML 里定位节点,正则几乎总是最差的选择:文档一换行缩进,模式就崩;而 XPath 是为结构化树设计的,选择器同时表达位置、属性和关系,换了排版仍成立。基础路径、谓词过滤、轴导航、常用函数、通配与运算这几组语法,最终都落到爬虫定位与配置文件解析两类场景上。判断选择器写得对不对有一条实用标准:能不能不依赖文本内容就命中——用谓词数下标(第二个匹配项)、用属性判断(带 class 的元素)、用轴表达关系(父节点、兄弟节点),比匹配具体文案稳定得多,因为文案会变而结构不会。性能上的判断同样明确:后代轴在文档较大时开销明显,定位已知层次时优先用子节点轴配合精确路径。另外要记住版本边界:XPath 1.0 没有正则匹配,需要模糊匹配时用 contains 组合,或换到支持 XPath 2.0 的处理器。

开发工具·共 34 条命令·最后更新 2026-07-21
xpathxmlhtml爬虫

基础路径 7

/
从根节点选取
//
从任意位置选取(递归)
.
当前节点
..
父节点
/bookstore/book
根下 bookstore 的 book 子元素
//book
文档中所有 book 元素
book/title
book 下的 title 子元素

谓词过滤 6

//book[1]
第一个 book
//book[last()]
最后一个 book
//book[position()<3]
前两个 book
//book[@lang="zh"]
lang 属性为 zh 的 book
//book[price>35]
price 大于 35 的 book
//book[title]
含有 title 子元素的 book

轴 Axes 7

ancestor::div
所有祖先 div
parent::node()
父节点
child::title
子节点 title
descendant::p
所有后代 p
following-sibling::div
之后的兄弟 div
preceding-sibling::h2
之前的兄弟 h2
self::a
自身(当且仅当是 a 时)

常用函数 8

contains(@class, "active")
class 属性包含 active
starts-with(text(), "Hello")
文本以 Hello 开头
normalize-space()
去除首尾空白并压缩中间空白
string-length(text())
文本长度
count(//book)
book 元素总数
concat("a", "-", "b")
字符串拼接
substring("hello", 1, 3)
截取子串
translate("abc", "abc", "ABC")
字符替换

通配与运算 6

*
匹配任意元素节点
@*
匹配任意属性
node()
匹配任意类型节点
//a | //b
a 和 b 的并集
//div[@id="x" and @class="y"]
and 条件
//div[@class="a" or @class="b"]
or 条件

命令示例

谓词组合:按条件过滤后再取第 N 个

//book[@lang="zh"] //book[price>35] //book[title] //book[last()]

第三、四行是两种常用过滤:含有 title 子元素、以及最后一个 book。下标类谓词都从 1 起数,且作用在过滤后的集合上。属性比较要求值可转成数字,price 写成带货币符号的字符串时表达式无效。

轴导航:从当前节点往上往外找

ancestor::div following-sibling::div preceding-sibling::h2

ancestor 向上取所有匹配的祖先,following-sibling 与 preceding-sibling 取当前位置之后与之前的兄弟节点。它们在「相邻区块」场景里比 // 更快也更准,因为不必遍历整棵子树。

常用函数:清洗文本与取聚合值

normalize-space() string-length(text()) count(//book) concat("a", "-", "b")

normalize-space 去掉首尾空白并压缩中间空白,抓取的 HTML 常有换行与缩进,不清洗就会导致文本匹配失败。count 返回数字,可用于先判断节点是否存在再继续取深层字段。

通配与逻辑运算组合定位

//div[@id="x" and @class="y"] //div[@class="a" or @class="b"] //a | //b

and 要求同时满足,or 是择一;竖线是并集,会把两边匹配到的节点合并去重后按文档顺序返回。需要「恰好是 a 而非包含 a」时用等值判断,需要宽松命中时换成 contains。

常见坑与注意事项

  • 谓词下标从 1 开始且作用于过滤后的结果集,顺序写反(先下标后谓词)语义会完全不同,改表达式前先确认这一层。
  • // 出现在深层嵌套里会显著放大遍历开销,文档较大时优先用精确路径加属性谓词定位。
  • XPath 1.0 不支持正则,需要模糊匹配时用 contains 组合,或确认目标运行时是否支持 2.0 再用匹配函数。
  • class 属性几乎总是复合的,等值判断匹配不到;用 contains 虽能命中,但会连带匹配 inactive 这类包含关系,需组合其它条件收窄。
  • 文本节点里的换行与缩进会让 starts-with 与直接文本匹配失效,先用 normalize-space 清洗再比较。

提示

  • // 比 / 慢很多,大范围文档优先用精确路径。
  • contains(@class, "x") 会匹配 "x y" 这种复合 class,比精确匹配更实用。
  • XPath 1.0 不支持正则,需要正则匹配得上 XPath 2.0 或用 contains 组合。
  • 定位"第 N 个匹配项"用谓词下标并从 1 起数:(//li[2])是第二个;索引按过滤后的结果集计算,先写谓词再写下标,顺序反了结果完全不同。
  • 抓取多个字段时把根节点先固定成一个变量,再在其下取相对路径,可避免每个表达式都从 // 重新扫描整棵树的重复开销。
  • 解析配置文件时优先用元素名做路径而不是属性值,元素名是 schema 的公开契约,属性更可能是实现细节,改动时先变属性。

常见问题

// 和 / 到底差在哪?为什么说 // 慢?

路径语义不同。/ 是从文档根出发的绝对路径,只沿一条确定的分支向下;而 // 是「从任意位置递归寻找后代」的缩写,每次求值都要遍历子树的剩余部分。也就是说 /bookstore/book 只看根下一个 book 再看它的子元素,而 //book 要检查文档里所有节点。文档小时差别可以忽略,节点上到万级后,// 出现在多层嵌套里会成倍放大遍历次数。所以已知层级就写精确路径,不确定层级再用 //,并尽量把它放在表达式开头一次定位,后续用相对路径展开。

谓词下标从 0 还是从 1 开始?//li[2] 指的是哪个?

从 1 开始,//li[2] 是第二个匹配的 li,不是第三个,这点和数组下标直觉相反。还有两个容易出错的细节:第一,下标是按过滤后的结果集计算的,所以先写谓词再写下标,//li[@class="x"][2] 是「带 x 类的 li 中的第二个」,顺序反过来语义就变了;第二,[] 里可以放多个条件表示同时满足,@price>35 这类数值比较要求属性本身可转成数字,否则表达式不成立而不是返回空集。

想按 class 精确匹配,为什么 contains 更实用?

因为真实 HTML 里的 class 通常是复合的:class="btn btn-primary active" 里三个类并存。contains(@class, "active") 判断的是属性值是否包含某个子串,能命中这种复合写法;而精确等值判断在整个属性值完全等于 active 时才成立,几乎永远匹配不到。因此 contains 是实际抓取中的默认选择。代价是它也会匹配到 active-tab、inactive 这类包含关系,所以它比精确匹配宽松、但不精确;需要更严时组合其它条件一起用。

XPath 1.0 里想做正则匹配怎么办?

1.0 没有正则函数,这是版本边界而不是写法问题。可行的替代有三种:用 contains 配合精确长度判断做包含匹配,能覆盖大多数实际需求;用 substring 做位置化裁剪,比较适合编号、日期这类固定格式;换到支持 XPath 2.0 的处理器(很多语言的新版库已支持),那里才有 matches 之类的正则能力。选实现前先确认目标运行时版本,否则同一表达式在两个环境下行为不一致。

抓多个字段时,怎么写才不至于每个表达式都扫全文档?

先把根节点固定成一个变量,再在它下面取相对路径。比如先拿到商品列表节点,之后的字段表达式全部相对该节点求值,遍历范围被限制在那一小段里,而不是每个字段都从 // 重新扫描整棵树。解析配置文件时还有一条经验:优先用元素名做路径而不是属性值,因为元素名通常是 schema 的公开契约、属性更可能是实现细节,改动时先变的是属性。

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。

由 巧匠 维护

公开更新于 2026年7月21日,内容持续校对官方文档。

联系我们

命令或描述有误?提交反馈、商务合作或产品建议都可发送邮件给我们。

联系我们