XPath(XML Path Language)是用于在 XML/HTML 文档中定位节点的语言,广泛用于爬虫与自动化测试。核心思想:通过路径表达式查找元素。
一、基础语法
| 表达式 | 说明 |
| / | 根节点开始,绝对路径 |
| // | 任意位置匹配,相对路径(常用) |
| . | 当前节点 |
| .. | 父节点 |
| * | 通配符,匹配任意标签 |
| @ | 选取属性 |
| text() | 获取文本内容 |
示例 HTML:
<div id=“header” class=“top-bar”>
<h1>网站标题</h1>
<a href=“/home” class=“nav”>首页</a>
<a href=“/about” class=“nav external”>关于我们</a>
</div>
<ul id=“menu”>
<li class=“item active”>商品A</li>
<li class=“item”>商品B</li>
<li class=“item out-of-stock”>商品C</li>
</ul>
二、核心定位方法
1. 标签 + 属性定位(最常用)
//标签名[@属性=‘值’]
- //a[@class=“nav”] → 匹配 class 为 nav 的 a 标签
- //div[@id=“header”] → 匹配 id 为 header 的 div
✅ 优先使用 id 或唯一 class,稳定高效。
2. 多属性组合(提高精度)
//标签名[@attr1=‘val1’ and @attr2=‘val2’]
- //a[@class=“nav” and @href=“/about”]
- //li[@class=“item” and contains(text(), “商品A”)]
支持 and、or、not() 逻辑。
3. 文本匹配
- 精确匹配:
//h1[text()=“网站标题”]
- 模糊包含:
//li[contains(text(), “商品”)]
适用于无属性但文本固定的元素。
4. 位置索引
- 第 n 个://ul/li[1](从 1 开始)
- 最后一个://ul/li[last()]
- 倒数第二个://ul/li[last()-1]
- 条件位置://tr[position() <= 3]
示例:
//table//tr[last()]/td[2]/text() → 最后一行第二列文本
5. 轴定位(Axes)——处理复杂结构
| 轴 | 说明 | 示例 |
| parent:: | 父节点 | //a/parent::div |
| child:: | 子节点 | //div/child::h1 |
| following-sibling:: | 后续兄弟 | //td[text()=“手机”]/following-sibling::td |
| preceding-sibling:: | 前面兄弟 | //td[2]/preceding-sibling::td[1] |
| descendant:: | 所有后代 | //div/descendant::a |
适用于兄弟、父子、隔代等关系定位。
6. 动态属性处理
页面元素 id、class 常带随机值(如 id=“btn_123”)。
解决方案:
- 包含匹配:
//button[contains(@id, “btn”)]_
- 开头匹配:
//input[starts-with(@id, “input_user”)]
- 精确匹配多 class:
//div[contains(concat(” ”, @class, ” ”), ” item ”)]
防止 item 被 highlight-item 误匹配。
三、常用函数
| 函数 | 作用 | 示例 |
| contains() | 是否包含 | //div[contains(@class, “top”)] |
| starts-with() | 是否开头 | //div[starts-with(@id, “menu”)] |
| text() | 获取文本 | //h1/text() |
| last() | 最后一个 | //li[last()] |
| position() | 位置判断 | //tr[position() > 1] |
| normalize-space() | 清除首尾空格 | normalize-space(//div/text()) |
四、高级技巧
1. 多条件“或”匹配
//div[@id=“header” or @class=“top-bar”]
2. 取属性值
//a/@href → 返回 href 属性值
//img/@src → 图片链接
3. 取文本内容
//h1/text() → “网站标题”
//p//text() → 所有后代文本合并
4. 嵌套查找
//div[@id=“menu”]//li[contains(text(), “商品A”)]
先定位父容器,再找子元素,提高稳定性。
五、调试与验证
1. 浏览器控制台测试
打开开发者工具(F12),在 Console 输入:
$x(‘//a[@class=“nav”]’) → 返回匹配元素数组
$x(‘//li’).length → 查看数量
2. 分步构建
复杂路径建议逐步验证:
- //table[@id=“menu”]
- //table[@id=“menu”]//tr
- //tr[td[contains(text(), “商品A”)]]/td[2]
六、常见问题与解决
| 问题 | 原因 | 解决 |
| 找不到元素 | 元素在 iframe 中 | 先切换 iframe |
| 元素未加载 | JS 动态渲染 | 加等待(WebDriverWait) |
| class 匹配失败 | 多 class 不完整匹配 | 用 contains 或 concat |
| XPath 易失效 | 使用绝对路径 | 改用相对路径 + 属性 |
| 文本含空格 | 实际有换行或缩进 | 用 normalize-space() |
七、最佳实践
- ✅ 优先使用 id、name、data-test** 等稳定属性**
- ✅ **使用 // 相对路径,避免 **/html/body/div/…
- ✅ 组合多个条件提高唯一性
- ✅ 复杂逻辑建议在代码中处理,而非硬写 XPath
- ❌ 避免过长、过深的路径
八、实战练习
- 获取所有导航链接的 href:
//div[@id=“header”]//a/@href
- 找出“缺货”商品名称:
//li[contains(@class, “out-of-stock”)]/text()
- 定位“关于我们”链接的父节点:
//a[text()=“关于我们”]/parent::div
- 取第二个菜单项的下一个兄弟:
//ul/li[2]/following-sibling::li[1]
九、总结
| 方法 | 适用场景 | 推荐度 |
| //tag[@attr=‘val’] | 有唯一属性 | ⭐⭐⭐⭐⭐ |
| //tag[contains(text(), ”…”)] | 文本固定 | ⭐⭐⭐⭐ |
| //tag[last()] | 最后一个元素 | ⭐⭐⭐ |
| axis:: | 兄弟/父子关系 | ⭐⭐⭐⭐ |
| contains(@attr, ”…”) | 动态属性 | ⭐⭐⭐⭐ |
核心原则:简洁、稳定、可维护。
部分信息可能已经过时









