# XPath 路径表达式
[[toc]]
XPath 是一种用于在 XML/HTML 文档中定位节点的语言。Legado 源支持使用 XPath 规则提取网页内容,以 `//`
开头的规则会自动识别为 XPath 表达式。
_注:本文所有代码均通过 Chrome(版本 123.0.6312.86) 验证_
## 1. 轴 (Axes)
XPath 规范定义了 13 种轴,用于定位元素树上相对于当前元素的节点。
| 轴 | 说明 | 缩写 |
|:---------------------|:---------------------------|:-----|
| `attribute` | 元素的属性 | `@` |
| `self` | 元素本身 | `.` |
| `parent` | 当前元素的父元素 | `..` |
| `child` | 当前元素的子元素 | — |
| `ancestor` | 当前元素的所有直属祖先 | — |
| `ancestor-or-self` | 当前元素及其所有直属祖先 | — |
| `descendant` | 当前元素的所有递归子元素 | — |
| `descendant-or-self` | 当前元素及其所有递归子元素 | — |
| `following` | 当前元素之后出现的所有元素(无视层级,不含直属后代) | — |
| `following-sibling` | 当前元素之后出现的所有同级元素 | — |
| `preceding` | 当前元素之前出现的所有元素(无视层级,不含直属祖先) | — |
| `preceding-sibling` | 当前元素之前出现的所有同级元素 | — |
| `namespace` | 不支持 | — |
**语法**:`轴名::表达式`
```js
> $x('//body/ancestor-or-self::*')
< [body, html]
```
## 2. 路径格式
XPath 通过"路径表达式"(Path Expression)选取元素,形式上与文件系统路径类似。
| 概念 | 说明 |
|:-----|:----------------------------------|
| `/` | 路径内部分隔符 |
| 绝对路径 | 以 `/` 起首,后跟根元素,如 `/step/step/...` |
| 相对路径 | 除绝对路径外的其他写法,如 `step/step` |
| `.` | 当前元素 |
| `..` | 当前元素的父元素 |
### 选取语法
| 符号 | 说明 |
|:-----------|:------------|
| `/` | 选取根元素 |
| `//` | 选取任意位置的某个元素 |
| `nodename` | 选取指定名称的元素 |
| `@` | 选取某个属性 |
## 3. 示例
以下示例基于这段 HTML:
```html
标题
```
| 表达式 | 结果 | 说明 |
|:-----------------------|:---------------|:--------------|
| `$x('/')` | `[document]` | 选取根元素 |
| `$x('/html')` | `[html]` | 绝对路径选取 |
| `$x('html/head/meta')` | `[meta, meta]` | 相对路径选取 |
| `$x('//p')` | `[p, p, p, p]` | 选取所有 p 元素 |
| `$x('html/body//a')` | `[a, a, a]` | 选取 body 下所有 a |
| `$x('//@lang')` | `[lang, lang]` | 选取属性 |
| `$x('//meta/..')` | `[head]` | 选取父元素 |
## 4. 谓语条件 (Predicate)
谓语条件是对路径表达式的附加筛选条件,写在方括号 `[]` 中。
| 表达式 | 结果 | 说明 |
|:-------------------------------|:-------------------------------|:--------|
| `html/head/meta[1]` | `` | 选取第一个 |
| `html/head/meta[last()]` | `` | 选取最后一个 |
| `html/head/meta[last()-1]` | `` | 选取倒数第二个 |
| `html/head/meta[position()>1]` | `` | 位置大于 1 |
| `//title[@lang]` | 两个 title | 具有特定属性 |
| `//title[@lang="eng"]` | English title | 属性值匹配 |
| `/html/body/div[dl]` | `` | 包含特定子元素 |
| `/html/body/div[p>35.00]` | Cpp 高级编程 div | 子元素值条件 |
## 5. 通配符
| 符号 | 说明 |
|:-----|:--------|
| `*` | 匹配任何元素 |
| `@*` | 匹配任何属性名 |
```js
$x('//*') // 选取所有元素
$x('/*/*') // 选取所有第二层元素
$x('//title[@*]') // 选取所有带属性的 title
```
## 6. 多路径选择
用 `|` 合并多个表达式的选取结果:
```js
$x('//title | //a') // 选取所有 title 和 a 元素
```
## 7. 函数
XPath 函数的参数可以是静态字符串或表达式,函数可嵌套调用。XPath 索引从 **1** 开始。
| 函数 | 说明 | 示例 |
|:---------------------|:----------|:------------------------------------------------------|
| `boolean()` | 转换为布尔值 | `boolean(//title)` → `true` |
| `number()` | 转换为数字 | `number(//p[1])` → `29.39` |
| `round()` | 四舍五入 | `round(//p[1])` → `29` |
| `ceiling()` | 向上取整 | `ceiling(//p[1])` → `30` |
| `floor()` | 向下取整 | `floor(//p[1])` → `29` |
| `concat()` | 字符串拼接 | `concat("cost:", //p[1], //p[2])` → `'cost:29.39usd'` |
| `contains()` | 判断是否包含 | `contains(//p[1], "29.39")` → `true` |
| `count()` | 统计元素个数 | `count(//p)` → `4` |
| `id()` | 根据 id 选取 | `id("list")` → `[dl#list]` |
| `last()` | 同级元素集合数量 | `//p[last()]` |
| `name()` | 返回元素名 | `name(//*[@id])` → `'dl'` |
| `normalize-space()` | 去除前后空白 | `normalize-space(" test ")` → `'test'` |
| `not()` | 返回布尔反值 | `//title[not(@lang)]` |
| `position()` | 返回元素位置 | `//meta[position()=2]` |
| `starts-with()` | 检查字符串开头 | `//title[starts-with(., "Cpp")]` |
| `string()` | 转换为字符串 | `string(//p)` → `'29.39'` |
| `string-length()` | 返回字符串长度 | `string-length(string(//p))` → `5` |
| `substring()` | 截取字符串 | `substring(string(//p), 1, 3)` → `'29.'` |
| `substring-after()` | 某字符之后的字符串 | `substring-after(string(//p), ".")` → `'39'` |
| `substring-before()` | 某字符之前的字符串 | `substring-before(string(//p), ".")` → `'29'` |
| `sum()` | 对数字求和 | `sum(//p[1])` → `69.34` |
| `translate()` | 依次替换字符 | `translate("aabbcc", "ac", "V8")` → `'VVbb88'` |