XPath 实例(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在网页数据抓取、自动化测试或文档解析等场景中,XPath 是一种不可或缺的工具。它通过类似路径的表达式,帮助开发者精准定位 HTML 或 XML 文档中的目标节点。然而,对于编程初学者而言,XPath 的语法和逻辑可能显得抽象复杂。本文将通过 “XPath 实例” 的详细讲解,结合实际案例和代码演示,逐步解析其核心概念与应用技巧,帮助读者从基础到进阶掌握这一技能。


什么是 XPath?

XPath(XML Path Language)是一种用于在 XML 或 HTML 文档中定位节点的语言。它通过路径表达式描述节点的层级关系,类似于文件系统中的目录路径。例如,/html/body/div 就是一个简单的 XPath 表达式,表示从根节点 html 开始,依次查找其子节点 bodydiv

XPath 的核心作用

  • 定位节点:如提取网页中的标题、价格或链接。
  • 筛选条件:根据属性值或文本内容过滤节点。
  • 数据提取:结合编程语言(如 Python 或 JavaScript),提取目标数据。

XPath 基础语法:路径与节点类型

1. 路径表达式的基本结构

XPath 的路径分为两种:绝对路径相对路径

  • 绝对路径:从根节点 / 开始,逐层指定节点路径,例如 /html/body/div/p
  • 相对路径:以当前上下文节点为起点,例如 //div[@class='content'](从任意位置查找 classcontentdiv)。

示例:HTML 结构与 XPath 路径

假设以下 HTML 片段:

<html>
  <body>
    <div class="container">
      <h1>欢迎来到示例页面</h1>
      <p>这是第一个段落。</p>
      <div class="content">
        <a href="/link">点击此处</a>
      </div>
    </div>
  </body>
</html>
  • 绝对路径:/html/body/div[@class='container']/h1 可定位到标题 <h1>
  • 相对路径://div[@class='content']/a 可直接定位到 <a> 标签,无需从根节点开始。

2. 节点类型与通配符

XPath 支持多种节点类型和通配符:
| 符号 | 含义 | 示例 |
|------|--------------------------|--------------------------|
| / | 根节点或直接子节点 | /html/body |
| // | 任意层级子节点 | //div(查找所有 div)|
| . | 当前节点 | .//p(当前节点下所有 p)|
| * | 任意节点类型 | //div/*div 下所有子节点)|
| @ | 属性节点 | //img/@src(获取 src 属性)|


定位技巧:轴(Axis)、谓词与条件筛选

1. 轴(Axis):定义节点搜索方向

轴决定了 XPath 表达式在文档中的搜索范围。常用轴包括:
| 轴 | 含义 | 示例 |
|-------------|--------------------------|--------------------------|
| child | 当前节点的子节点 | child::div |
| parent | 父节点 | parent::body |
| ancestor | 所有祖先节点 | ancestor::div |
| descendant| 当前节点的后代节点 | descendant::p |
| following | 当前节点之后的所有节点 | following::div |

示例:使用 ancestor

//div[@id='target']/ancestor::div

该表达式会找到 idtargetdiv 的所有祖先 div 节点。

2. 谓词(Predicate):添加筛选条件

谓词通过 [...] 语法添加条件,例如:

  • //div[@class='active']:查找 class 属性为 activediv
  • //a[2]:选取第二个 <a> 标签。
  • //p[contains(text(), '示例')]:包含特定文本的段落。

实例:动态筛选商品价格

假设网页中商品价格存储在类名为 price<span> 标签中:

<div class="product">
  <h2>商品名称</h2>
  <span class="price">¥ 99.00</span>
</div>

XPath 表达式://span[@class='price']/text() 可直接提取文本内容 ¥ 99.00


进阶用法:函数、命名空间与条件表达式

1. 常用函数

XPath 提供多种函数辅助数据处理:

  • text():获取节点文本内容。
  • contains():判断属性或文本是否包含子字符串。
  • starts-with():判断是否以特定字符串开头。
  • position():获取节点在当前集合中的位置索引。

示例:提取以 http 开头的链接

//a[starts-with(@href, 'http')]/@href

该表达式会筛选出所有 href 属性以 http 开头的链接。

2. 处理命名空间

当 XML 文档包含命名空间时,需通过 local-name()namespace-uri() 处理。例如:

//*[local-name()='Book' and contains(@category, 'Technology')]

此表达式忽略命名空间,直接定位标签名为 Book 且类别含 Technology 的节点。


实际案例:电商商品信息抓取

场景描述

假设需从某电商页面提取商品标题、价格和链接,其 HTML 结构如下:

<div class="product-list">
  <div class="product-item">
    <h3 class="title">智能手表 Pro X</h3>
    <span class="price">¥ 1,299</span>
    <a href="/products/123" class="detail-link">查看详情</a>
  </div>
  <div class="product-item">
    <h3 class="title">无线耳机 Mini</h3>
    <span class="price">¥ 399</span>
    <a href="/products/456" class="detail-link">查看详情</a>
  </div>
</div>

步骤解析

  1. 定位商品容器//div[@class='product-list']
  2. 遍历每个商品项//div[@class='product-item']
  3. 提取标题//div[@class='product-item']/h3/text()
  4. 提取价格//div[@class='product-item']/span[@class='price']/text()
  5. 提取链接//div[@class='product-item']/a/@href

Python 实现示例

使用 lxml 库解析 HTML 并提取数据:

from lxml import html  

tree = html.fromstring(html_content)

products = tree.xpath("//div[@class='product-item']")

for product in products:
    title = product.xpath(".//h3[@class='title']/text()")[0]
    price = product.xpath(".//span[@class='price']/text()")[0]
    link = product.xpath(".//a/@href")[0]
    print(f"标题:{title} | 价格:{price} | 链接:{link}")

常见问题与优化建议

问题 1:XPath 表达式返回空值

可能原因:

  • 节点路径错误(如拼写错误或层级不符)。
  • 动态加载内容未完全加载(需等待或使用 JavaScript 渲染工具)。

问题 2:如何提高 XPath 的稳定性?

  • 避免绝对路径:优先使用 // 和属性筛选,而非层级路径。
  • 减少依赖类名:若类名易变,可结合文本内容或相邻节点定位。
  • 使用模糊匹配contains()starts-with() 替代精确匹配。

结论

通过本文的 “XPath 实例” 分析,读者应能掌握从基础语法到实际应用的完整流程。XPath 的核心在于理解节点关系与路径逻辑,而熟练运用则需通过不断实践和调试。无论是自动化测试、数据抓取还是文档解析,XPath 都是开发者工具箱中不可或缺的利器。建议读者结合具体项目需求,尝试编写更多实例,逐步提升对复杂场景的应对能力。

提示:本文案例代码可在本地环境复现,修改 HTML 结构或表达式即可验证不同效果。

最新发布