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 运行步骤与结果

  1. 复制代码保存为douban_top250.py
  2. 打开命令行,切换到代码所在文件夹,执行python douban_top250.py
  3. 爬取完成后,在同级目录找到豆瓣Top250.txt,打开即可查看结构化的电影信息。

三、实战案例 2:4399 登录抓取(需权限数据进阶)

很多网站的核心数据(如个人中心、订单记录)需要登录才能访问。本案例以 4399 平台为例,学习 “表单提交登录→会话保持→登录后页面抓取”,掌握进阶爬虫的关键技术。

3.1 案例目标

  1. 安全获取 4399 账号密码(避免硬编码);
  2. 提交登录表单,保持会话状态;
  3. 访问登录后的 “个人标签页”,并保存页面内容到本地。

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中的usernamepassword需与 4399 登录页面的表单字段一致(可通过浏览器 F12 查看):

  1. 打开 4399 登录页,按 F12→“元素” 标签;
  2. 搜索<input type="text"(用户名框)和<input type="password"(密码框);
  3. 查看其name属性,确保与login_data的键一致。

3.4 常见问题与解决方案

问题现象可能原因解决方案
报错 “请设置环境变量”环境变量未设置或未生效重新设置变量,重启命令行 / PyCharm
登录失败(400/403 状态码)表单字段不匹配或被反爬识别检查表单字段,更换真实User-Agent
保存的页面是登录页登录未成功,会话无有效 Cookie检查账号密码,查看登录响应是否有 “密码错误” 提示
中文乱码页面编码未设置为 UTF-8确保添加target_response.encoding = 'utf-8'

四、爬虫实战技巧与避坑指南(通用总结)

无论爬取公开数据还是登录后数据,以下技巧能帮你提高爬虫稳定性,规避常见问题:

4.1 反爬规避三大原则

  1. 模拟人类行为:添加合理延迟(time.sleep(1-3)),避免高频请求;
  2. 完善请求头:除User-Agent外,可添加Referer(来源页)、Accept(接受的内容类型);
  3. 使用代理 IP:若 IP 被封,可通过代理池切换 IP(如免费代理:https://www.zdaye.com/)。

4.2 异常处理与日志记录

  • 捕获异常:用try-except包裹请求逻辑,处理网络错误、超时、状态码异常;
  • 日志记录:用logging替代print,记录操作时间、状态码、错误信息,方便调试。

4.3 合规爬虫的底线

  1. 遵守robots.txt:访问网站https://域名/robots.txt,查看禁止爬取的路径;
  2. 非商业用途:个人学习可爬取公开数据,禁止用于商业盈利或恶意攻击;
  3. 控制爬取频率:避免给网站服务器造成压力,一般建议每秒不超过 1 次请求。

五、进阶学习方向

掌握本文内容后,可向以下方向深入:

  1. 多线程 / 多进程爬取:用threadingconcurrent.futures提高爬取效率;
  2. 数据存储升级:将文本文件存储改为数据库(MySQL、MongoDB),支持结构化查询;
  3. 验证码处理:集成打码平台(如超级鹰),自动识别图形验证码;
  4. 动态页面爬取:用SeleniumPlaywright爬取 JavaScript 渲染的页面(如 Vue/React 网站)。

总结

本文从 XPath 解析基础入手,通过豆瓣 Top250(入门)和 4399 登录抓取(进阶)两个案例,覆盖了 Python 爬虫的核心流程:从 HTTP 请求发送、HTML 解析,到数据存储、会话保持。掌握这些技术后,你可以轻松应对大多数常见的爬虫场景。

爬虫的核心不仅是 “技术实现”,更是 “平衡效率与合规”—— 在获取数据的同时,尊重网站规则,避免给服务器造成负担。随着实践深入,你会逐渐掌握更复杂的爬取技巧,让 Python 成为你数据获取的强大工具。

更多推荐