Python 爬虫从入门到进阶:XPath 解析与实战案例(豆瓣 Top250+4399 登录抓取)
Python 爬虫从入门到进阶:XPath 解析与实战案例(豆瓣 Top250+4399 登录抓取)
在数据获取与分析领域,Python 爬虫是高效收集信息的核心工具。从公开的影视榜单(如豆瓣 Top250)到需要登录权限的个人数据(如 4399 个人中心),掌握爬虫技术能帮你轻松获取目标信息。本文将从核心技术铺垫(XPath 解析、requests 库使用)入手,通过两个递进式实战案例(豆瓣 Top250 爬取→4399 登录抓取),带你掌握从 “公开数据爬取” 到 “登录后数据获取” 的完整流程,同时覆盖安全存储、反爬规避、异常处理等实用技巧。
一、爬虫基础:工具与核心技术
在开始实战前,需先掌握爬虫的 “三大基石”:工具安装、HTTP 请求发送、HTML 解析(XPath)。这些技术是后续所有案例的核心依赖。
1.1 必备工具与安装
(1)Python 环境
前往Python 官网下载 Python 3.8 + 版本,安装时勾选 “Add Python to PATH”,确保命令行可调用。
(2)核心库安装
本文用到 3 个关键库:requests(发送 HTTP 请求)、lxml(HTML 解析与 XPath 支持)、logging(日志记录,内置库无需安装)。打开命令提示符(CMD / 终端)执行:
pip install requests lxml
1.2 核心技术:XPath 解析(爬虫的 “定位神器”)
XPath(XML Path Language)是解析 HTML/XML 的高效语言,能精准定位页面元素,比 CSS 选择器更灵活。以下结合示例讲解 XPath 核心语法,后续案例会反复用到。
(1)XPath 基础:定位逻辑与语法
我们先定义一段测试 HTML,作为解析对象:
from lxml import etree
# 测试HTML结构
test_html = """
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html"><span>third item</span></a></li>
</ul>
</div>
"""
# 创建解析对象
tree = etree.HTML(test_html)
(2)常用 XPath 查询方式(必学)
| 需求场景 | XPath 表达式 | 结果说明 |
|---|---|---|
所有li元素 | //li | 返回所有<li>标签对象列表 |
ul下的li元素 | //ul/li | 仅返回<ul>直接子节点的<li> |
class="item-0"的li | //li[@class='item-0'] | 通过属性过滤,返回指定class的<li> |
a标签的文本内容 | //li/a/text() | 提取<a>标签内的文本(如["first item"]) |
a标签的href属性 | //li/a/@href | 提取<a>标签的href值(如["link1.html"]) |
包含 “item” 的class | //li[contains(@class, 'item')] | 模糊匹配属性,适合多class场景 |
(3)XPath 关键注意事项
- 索引从 1 开始:与 Python 的 0 索引不同,XPath 中第一个元素是
[1](如//li[1]取第一个li); //与/区别://搜索整个文档,/仅搜索直接子节点;- 空结果处理:无匹配元素时返回空列表,需在代码中判断避免报错。
二、实战案例 1:豆瓣电影 Top250 爬取(公开数据入门)
豆瓣电影 Top250 是爬虫入门的经典案例 —— 数据公开、结构清晰,无需登录即可获取。本案例将实现 “分页爬取→数据解析→本地存储” 的完整流程,掌握基础爬虫的核心逻辑。
2.1 案例目标
获取豆瓣 Top250 的电影信息:标题、导演与演员、评分、经典台词,并保存到本地文本文件。
2.2 完整代码
import requests
from lxml import etree
import time
import logging
# 1. 基础配置(日志+请求头)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
}
base_url = 'https://movie.douban.com/top250' # 榜单首页
output_file = '豆瓣Top250.txt' # 数据存储文件
# 2. 核心函数:爬取单页数据
def fetch_single_page(start: int) -> None:
"""start:分页起始位置(0→第1页,25→第2页,...,225→第10页)"""
url = f'{base_url}?start={start}'
try:
# 发送请求(添加1秒延迟防反爬)
time.sleep(1)
response = requests.get(url=url, headers=headers)
response.raise_for_status() # 状态码非200则抛异常
response.encoding = 'utf-8' # 避免中文乱码
# 3. XPath解析页面
tree = etree.HTML(response.text)
movie_list = tree.xpath('//div[@class="info"]') # 每部电影的信息容器
for movie in movie_list:
# 提取标题(处理多语言标题拼接)
title = ''.join(movie.xpath('./div[@class="hd"]/a/span[@class="title"]/text()')).replace('\xa0', '')
# 提取导演与演员(清理多余空格)
director_cast = ''.join(movie.xpath('./div[@class="bd"]/p[1]/text()')[0].split()).replace('\xa0', '')
# 提取评分(处理空值)
rating = movie.xpath('./div[@class="bd"]/div[@class="star"]/span[@class="rating_num"]/text()')
rating = rating[0] if rating else '无评分'
# 提取经典台词(处理空值)
quote = movie.xpath('./div[@class="bd"]/p[@class="quote"]/span/text()')
quote = quote[0] if quote else '无'
# 4. 数据存储(追加写入文本文件)
movie_data = f"""
【电影标题】:{title}
【导演演员】:{director_cast}
【豆瓣评分】:{rating}
【经典台词】:{quote}
{'-'*50}
"""
with open(output_file, 'a', encoding='utf-8') as f:
f.write(movie_data)
logging.info(f"成功保存:{title}")
except requests.RequestException as e:
logging.error(f"爬取第{start//25+1}页失败:{str(e)}")
# 5. 主函数:控制分页爬取
def main():
logging.info("开始爬取豆瓣电影Top250...")
# 豆瓣Top250共10页,每页25部,start从0到225(步长25)
for start in range(0, 250, 25):
fetch_single_page(start)
logging.info(f"爬取完成!数据已保存至:{output_file}")
if __name__ == '__main__':
main()
2.3 代码解析与关键技巧
(1)分页逻辑:start参数控制
豆瓣 Top250 通过 URL 的start参数分 10 页,每页 25 部电影:
- 第 1 页:
https://movie.douban.com/top250?start=0 - 第 10 页:
https://movie.douban.com/top250?start=225 - 用
range(0, 250, 25)生成所有分页起始位置。
(2)反爬规避:请求头与延迟
User-Agent:模拟浏览器请求,避免被豆瓣识别为 “机器人”(可从浏览器 F12→“网络”→复制真实User-Agent);time.sleep(1):每页爬取后延迟 1 秒,模拟人类浏览速度,降低 IP 被封风险。
(3)数据处理:空值与乱码
- 空值处理:用
if...else判断评分、台词是否存在(如rating = rating[0] if rating else '无评分'); - 乱码处理:设置
response.encoding = 'utf-8',并去除特殊空格\xa0(页面常见乱码原因)。
2.4 运行步骤与结果
- 复制代码保存为
douban_top250.py; - 打开命令行,切换到代码所在文件夹,执行
python douban_top250.py; - 爬取完成后,在同级目录找到
豆瓣Top250.txt,打开即可查看结构化的电影信息。
三、实战案例 2:4399 登录抓取(需权限数据进阶)
很多网站的核心数据(如个人中心、订单记录)需要登录才能访问。本案例以 4399 平台为例,学习 “表单提交登录→会话保持→登录后页面抓取”,掌握进阶爬虫的关键技术。
3.1 案例目标
- 安全获取 4399 账号密码(避免硬编码);
- 提交登录表单,保持会话状态;
- 访问登录后的 “个人标签页”,并保存页面内容到本地。
3.2 完整代码
import requests
import os
import logging
# 1. 基础配置(日志+安全校验)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 2. 安全获取账号密码(从环境变量读取,避免硬编码泄露)
username = os.getenv('4399_USERNAME') # 环境变量名:4399_USERNAME
password = os.getenv('4399_PASSWORD') # 环境变量名:4399_PASSWORD
if not username or not password:
logging.error("请先设置环境变量:4399_USERNAME(账号)和4399_PASSWORD(密码)")
exit(1)
# 3. 核心URL与请求头
login_url = 'https://ptlogin.4399.com/ptlogin/login.do?v=1' # 登录接口
target_url = 'https://my.4399.com/forums/mtags' # 登录后才能访问的目标页
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
}
# 4. 核心函数:登录与页面抓取
def login_and_fetch():
# 创建会话对象(关键!保持登录状态,自动携带Cookie)
session = requests.Session()
try:
# 5. 提交登录表单
login_data = {
'username': username,
'password': password
}
login_response = session.post(url=login_url, data=login_data, headers=headers)
login_response.raise_for_status()
logging.info(f"登录成功!状态码:{login_response.status_code}")
# 6. 访问目标页面(利用已登录的会话)
target_response = session.get(url=target_url, headers=headers)
target_response.raise_for_status()
target_response.encoding = 'utf-8' # 避免中文乱码
logging.info(f"目标页面获取成功!状态码:{target_response.status_code}")
# 7. 保存页面内容到本地
output_file = '4399个人标签页.html'
with open(output_file, 'w', encoding='utf-8') as f:
f.write(target_response.text)
logging.info(f"页面已保存至:{os.path.abspath(output_file)}")
except requests.RequestException as e:
logging.error(f"操作失败:{str(e)}")
exit(1)
if __name__ == '__main__':
login_and_fetch()
3.3 关键技术解析
(1)安全存储:环境变量的使用
为什么不用硬编码?
若直接写password = "123456",代码上传到 GitHub 或分享时会泄露密码。环境变量是系统级的 “临时存储”,仅当前设备可见。
环境变量设置方法:
- Windows:打开 CMD(管理员),执行:
bash
setx 4399_USERNAME "你的4399账号" setx 4399_PASSWORD "你的4399密码" - Linux/macOS:打开终端,执行:
bash
export 4399_USERNAME="你的4399账号" export 4399_PASSWORD="你的4399密码"
设置后需重启命令行 / PyCharm,确保变量生效。
(2)会话保持:requests.Session()的核心作用
浏览器登录后会保存Cookie,后续访问自动携带以证明 “已登录”。requests.Session()模拟这一过程:
- 会话内的所有请求共享
Cookie; - 无需手动处理
Cookie,简化登录逻辑。
(3)登录表单:data参数的匹配
login_data中的username和password需与 4399 登录页面的表单字段一致(可通过浏览器 F12 查看):
- 打开 4399 登录页,按 F12→“元素” 标签;
- 搜索
<input type="text"(用户名框)和<input type="password"(密码框); - 查看其
name属性,确保与login_data的键一致。
3.4 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 报错 “请设置环境变量” | 环境变量未设置或未生效 | 重新设置变量,重启命令行 / PyCharm |
| 登录失败(400/403 状态码) | 表单字段不匹配或被反爬识别 | 检查表单字段,更换真实User-Agent |
| 保存的页面是登录页 | 登录未成功,会话无有效 Cookie | 检查账号密码,查看登录响应是否有 “密码错误” 提示 |
| 中文乱码 | 页面编码未设置为 UTF-8 | 确保添加target_response.encoding = 'utf-8' |
四、爬虫实战技巧与避坑指南(通用总结)
无论爬取公开数据还是登录后数据,以下技巧能帮你提高爬虫稳定性,规避常见问题:
4.1 反爬规避三大原则
- 模拟人类行为:添加合理延迟(
time.sleep(1-3)),避免高频请求; - 完善请求头:除
User-Agent外,可添加Referer(来源页)、Accept(接受的内容类型); - 使用代理 IP:若 IP 被封,可通过代理池切换 IP(如免费代理:
https://www.zdaye.com/)。
4.2 异常处理与日志记录
- 捕获异常:用
try-except包裹请求逻辑,处理网络错误、超时、状态码异常; - 日志记录:用
logging替代print,记录操作时间、状态码、错误信息,方便调试。
4.3 合规爬虫的底线
- 遵守
robots.txt:访问网站https://域名/robots.txt,查看禁止爬取的路径; - 非商业用途:个人学习可爬取公开数据,禁止用于商业盈利或恶意攻击;
- 控制爬取频率:避免给网站服务器造成压力,一般建议每秒不超过 1 次请求。
五、进阶学习方向
掌握本文内容后,可向以下方向深入:
- 多线程 / 多进程爬取:用
threading或concurrent.futures提高爬取效率; - 数据存储升级:将文本文件存储改为数据库(MySQL、MongoDB),支持结构化查询;
- 验证码处理:集成打码平台(如超级鹰),自动识别图形验证码;
- 动态页面爬取:用
Selenium或Playwright爬取 JavaScript 渲染的页面(如 Vue/React 网站)。
总结
本文从 XPath 解析基础入手,通过豆瓣 Top250(入门)和 4399 登录抓取(进阶)两个案例,覆盖了 Python 爬虫的核心流程:从 HTTP 请求发送、HTML 解析,到数据存储、会话保持。掌握这些技术后,你可以轻松应对大多数常见的爬虫场景。
爬虫的核心不仅是 “技术实现”,更是 “平衡效率与合规”—— 在获取数据的同时,尊重网站规则,避免给服务器造成负担。随着实践深入,你会逐渐掌握更复杂的爬取技巧,让 Python 成为你数据获取的强大工具。
更多推荐
所有评论(0)