XPath是处理复杂页面结构和精准数据提取的必备技能,尤其适合需要应对不规则HTML或需要复杂定位逻辑的场景。对于简单页面,CSS选择器可能更高效,但掌握XPath能显著提升爬虫的适应能力和开发效率。
目标:学会用XPath精准定位HTML元素,从“大海捞针”到“一秒锁定”!
学习目标:掌握XPath语法、lxml库安装与使用,从此告别“复制粘贴抓数据”的苦力活!
7.1 XPath是什么?为什么它比CSS选择器更“硬核”?
核心作用:
- 精准定位:通过路径表达式快速找到HTML/XML中的节点(比如“找到所有
<div class='price'>
”)(可以帮助我们更加快速定位到所需要的关键词) - 灵活筛选:支持按属性、文本内容、层级关系过滤(比如“找价格大于100元的商品”)
- 动态适配:处理不规范的HTML代码(比如标签嵌套混乱的页面)
举个栗子🌰:
html
<!-- 目标HTML -->
<div class="book">
<h2>Python编程从入门到放弃</h2>
<p class="price">¥59.9</p>
</div>
python
from lxml import etree
html = etree.HTML(html_content)
title = html.xpath('//div[@class="book"]/h2/text()')[0] # 输出:Python编程从入门到放弃
price = html.xpath('//div[@class="book"]/p[@class="price"]/text()')[0] # 输出:¥59.9
7.2 安装lxml:别让环境问题“劝退”你!
安装命令:
bash
pip install lxml
常见报错与解决:
- 报错1:
Could not find function xmlCheckVersion in library libxml2
- 解决:安装系统级依赖(Linux:
sudo apt-get install libxml2-dev
,Mac:brew install libxml2
)
- 解决:安装系统级依赖(Linux:
- 报错2:
lxml.etree.XMLSyntaxError
- 解决:检查HTML是否不规范(用浏览器开发者工具的“查看页面源代码”复制完整内容)
7.3 XPath语法基础:从“小白”到“定位大师”
7.3.1 节点选择:像“切蛋糕”一样分层
表达式 | 说明 | 示例代码(提取所有<a> 标签) |
---|---|---|
/ | 绝对路径(从根节点开始) | //html/body/div |
// | 相对路径(任意层级) | //div[@class='book'] |
. | 当前节点 | ./span |
.. | 父节点 | span/.. |
7.3.2 属性与文本筛选:精准“抓特征”
python
# 按属性筛选(找所有链接)
links = html.xpath('//a/@href') # 提取所有href属性值
# 按文本内容筛选(找包含“免费”的段落)
free_text = html.xpath('//p[contains(text(), "免费")]')
7.3.3 多条件组合:用“逻辑运算符”加强筛选
python
# 找class包含"price"且标签是<p>的元素
prices = html.xpath('//p[contains(@class, "price")]')
7.4 获取“特殊”HTML:处理动态内容与不规则标签
7.4.1 提取属性值:不止是文本内容
python
# 提取图片链接
img_src = html.xpath('//img[@id="cover"]/@src')[0]
7.4.2 处理注释与特殊符号:用转义符
html
<!-- HTML中的注释 -->
<div><!-- 这是价格 -->¥99.9</div>
python
# 提取注释内容(需配合contains函数)
comment = html.xpath('//div[contains(text(), "这是价格")]/text()')
7.5 XPath函数:让定位更“智能”
常用函数速查表:
函数 | 作用 | 示例代码 |
---|---|---|
contains() | 包含指定文本 | //a[contains(text(), "登录")] |
starts-with() | 属性值以某字符串开头 | //div[starts-with(@id, "nav")] |
position() | 节点位置(索引从1开始) | //li[position()<=3] |
last() | 最后一个匹配节点 | //div[@class='item'][last()] |
实战案例:提取第2到第4个商品名称
python
products = html.xpath('//div[@class="product"][position()>=2 and position()<=4]/h3/text()')
对比CSS选择器
特性 | XPath | CSS选择器 |
---|---|---|
轴查询 | 支持 | 不支持 |
位置索引 | 从1开始计数 | 从1开始计数 |
文本模糊匹配 | contains() | :contains伪类 |
性能 | 稍慢 | 更快 |
学习曲线 | 较陡峭 | 较平缓 |
7.6 实战项目:爬取豆瓣电影Top250信息
目标需求:
- 提取电影名称、评分、简介
- 处理分页(翻页逻辑)
- 保存数据到CSV文件
代码实现:
python
import requests
from lxml import etree
import csv
url = "https://movie.douban.com/top250"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
# 发送请求
response = requests.get(url, headers=headers)
html = etree.HTML(response.text)
# 提取数据
movies = html.xpath('//div[@class="item"]')
with open("douban_top250.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["排名", "名称", "评分", "简介"])
for movie in movies:
rank = movie.xpath('.//em/text()')[0]
title = movie.xpath('.//span[@class="title"]/text()')[0]
rating = movie.xpath('.//span[@class="rating_num"]/text()')[0]
quote = movie.xpath('.//span[@class="inq"]/text()')[0] if len(movie.xpath('.//span[@class="inq"]/text()')) > 0 else "无简介"
writer.writerow([rank, title, rating, quote])
print("数据已保存!共抓取", len(movies), "条数据")
7.7 避坑指南:XPath的“常见雷区”
- 索引越界:
xpath()
返回列表,直接取[0]
前需检查长度 - 动态内容:XPath无法处理JavaScript渲染的内容(需配合Selenium)
- 编码问题:确保响应内容的编码与解析时一致(用
response.encoding = 'utf-8'
)
7.8 学习资源推荐:成为XPath高手的捷径
- 在线测试工具:
- XPath Helper(Chrome插件,实时测试XPath)
- 书籍推荐:
- 《XPath和XSLT实战教程》(中文电子书,GitHub可免费下载)
- 避坑秘籍:
- 优先用浏览器开发者工具的“复制XPath”功能(右键元素 → Copy → Copy XPath)
- 用
//
代替冗长的绝对路径(更健壮,适应HTML结构变化)
7.9 本章小结
-
核心技能:
- 掌握XPath语法与常用函数
- 能用lxml解析复杂HTML
- 实现豆瓣电影Top250数据抓取
-
高阶技巧:
- 结合正则表达式提取模糊内容(如
re.findall(r'\d+', price)
) - 用
lxml.objectify
处理XML数据(如API响应)
- 结合正则表达式提取模糊内容(如
关于xpath的搞笑彩蛋:
如果XPath会说话,可能会吐槽:“人类总让我精准定位元素,却忘了告诉我页面代码是‘祖传’的!”