Python爬虫存数据:将抓取结果写入MySQL数据库的步骤

南乔
南乔
发布时间:2026-09-20 | 3人浏览过
原创

编写爬虫程序时,内存中的字典或列表无法持久保存数据。将解析后的抓取结果落盘到关系型数据库(如 MySQL),可以为后续的数据检索、清洗、去重和业务分析提供可靠支持。完成这一目标的核心流程包括:设计与源数据字段对应的表结构、在 Python 中建立受管的数据库连接、使用参数化 SQL 避免注入风险,以及通过批量提交提升写入吞吐量。

一、数据落库的端到端交互架构

在编写具体代码之前,需要明确爬虫与数据库之间的数据流向。网络爬虫通常由 HTTP 请求、HTML/JSON 解析、字段结构化处理、入库管道(Pipeline)四部分组成。数据库写入环节位于整个管道的末端,负责确保网络断开或解析异常时不破坏现有数据的一致性。

SVG 配图 1
图1:爬虫系统中的数据流动与落库生命周期

二、环境准备与 MySQL 目标表设计

在 Python 中与 MySQL 建立交互,最常用的纯 Python 驱动是 PyMySQL。它不需要在宿主机上编译 C 库,安装简单且跨平台兼容性良好。使用 pip 在命令行中执行以下命令完成安装:

pip install pymysql

接下来需要在 MySQL 中建立对应的数据库和数据表。这里以爬取图书信息的场景为例(包含书名、价格、ISBN编号及采集时间):

CREATE DATABASE IF NOT EXISTS crawler_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE crawler_db;

CREATE TABLE IF NOT EXISTS books (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    isbn VARCHAR(32) NOT NULL,
    title VARCHAR(255) NOT NULL,
    price DECIMAL(8, 2) NOT NULL DEFAULT 0.00,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uq_isbn (isbn)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
字符集与唯一键提示:数据库字符集务必选择 utf8mb4,否则一旦网页文本中包含表情符号(Emoji)或特殊数学字符,写入时会抛出 OperationalError: 1366, Incorrect string value。对 isbn 设置唯一索引(UNIQUE KEY),可以天然防止爬虫断点重跑时产生重复数据。

三、核心实现:单条数据的入库操作

数据写入主要分为四个动作:创建数据库连接、获取游标(Cursor)、执行参数化 SQL 插入、提交事务(Commit)。执行后必须关闭游标与连接,释放底层 TCP 套接字。

SVG 配图 2
图2:PyMySQL 单次操作执行流程与资源生命周期

以下是使用参数化查询将抓取项写入数据库的完整可运行代码:

import pymysql

# 模拟爬虫解析提取出来的字典数据
crawled_item = {
    "isbn": "9787115546082",
    "title": "流畅的Python(第二版)",
    "price": 139.00
}

# 1. 建立数据库连接
connection = pymysql.connect(
    host="127.0.0.1",
    user="root",
    password="your_password",
    database="crawler_db",
    charset="utf8mb4",
    autocommit=False  # 明确关闭自动提交,手动掌控事务
)

try:
    with connection.cursor() as cursor:
        # 2. 编写参数化 SQL 模板,避免拼接字符串引起的 SQL 注入
        insert_sql = """
            INSERT INTO books (isbn, title, price)
            VALUES (%s, %s, %s)
            ON DUPLICATE KEY UPDATE 
                title = VALUES(title),
                price = VALUES(price);
        """
        # 3. 将变量以元组形式传入 execute 方法
        data_tuple = (
            crawled_item["isbn"],
            crawled_item["title"],
            crawled_item["price"]
        )
        cursor.execute(insert_sql, data_tuple)

    # 4. 显式提交事务
    connection.commit()
    print("数据入库成功!")

except pymysql.MySQLError as error:
    # 发生异常时回滚,避免事务挂起占用锁
    connection.rollback()
    print(f"写入发生错误,已回滚事务: {error}")

finally:
    # 5. 确保在任何情况下均关闭数据库连接
    connection.close()
切忌使用 f-string 或 % 拼接 SQL:拼接字符串不仅存在严重的安全隐患(SQL注入),而且如果书名中带有英文单双引号(如 O'Reilly),拼接后的语法会直接报错崩溃。通过 cursor.execute(sql, (param1, param2)) 传参,驱动会自动负责转义和类型校验。

四、批量写入与性能调优

当爬虫采集的数据量达到几千甚至几十万条时,如果每解析一条数据就发起一次 INSERT 并执行一次 commit(),程序的主要时间将全部耗费在网络往返延迟(RTT)和磁盘写日志上。此时应当使用批量插入(Batch Insert)模式。

批量写入通过 cursor.executemany() 将多条记录打包在同一个网络包中发送给 MySQL 服务器,并在累积达到一定批次(例如 500 条)时统一执行一次 commit()。

import pymysql

# 模拟爬虫抓取到的多条记录列表
items_buffer = [
    ("9787111636663", "深入理解计算机系统", 179.00),
    ("9787121362142", "算法导论(原书第3版)", 128.00),
    ("9787115545566", "Python核心编程", 99.00)
]

connection = pymysql.connect(
    host="127.0.0.1",
    user="root",
    password="your_password",
    database="crawler_db",
    charset="utf8mb4"
)

try:
    with connection.cursor() as cursor:
        batch_insert_sql = """
            INSERT INTO books (isbn, title, price)
            VALUES (%s, %s, %s)
            ON DUPLICATE KEY UPDATE 
                title = VALUES(title),
                price = VALUES(price);
        """
        # executemany 接收包含多个元组的列表
        rows_affected = cursor.executemany(batch_insert_sql, items_buffer)
        
    connection.commit()
    print(f"批量入库成功,受影响行数: {rows_affected}")

except pymysql.MySQLError as err:
    connection.rollback()
    print(f"批量写入失败: {err}")
finally:
    connection.close()

五、单条插入与批量插入的核心机制对比

为什么批量写入的速度会有数十倍的提升?下表对比了两种模式在实际开发中的表现与取舍:

对比维度 单条插入(逐条 Commit) 批量插入(executemany + 批量 Commit)
网络通信开销 N 条数据对应 N 次 TCP 报文往返,网络延时严重放大 数据打包合并,大幅缩减网络往返次数
磁盘 I/O 压力 频繁触发 Redo Log 刷盘(fsync),磁盘压力大 按批次合并刷盘,有效利用 I/O 带宽
吞吐量(每秒写入) 约 50 ~ 200 行/秒(受限于网络与磁盘) 可达 2000 ~ 10000+ 行/秒
异常回滚颗粒度 单条隔离,一条报错不影响其他已提交数据 整个批次同时回滚,需要对异常数据进行分流或重试
SVG 配图 3
图3:单条入库与批量入库的 I/O 消耗模式对比

六、爬虫入库全流程规范与常见陷阱排查

在长时间运行的生产爬虫中,以下三个关键隐患直接决定了入库管道的稳定性:

  • MySQL 连接超时(2006, 'MySQL server has gone away'):爬虫在解析复杂网页或下载大文件时耗时较长,如果两个请求之间的间隔超过了 MySQL 服务端的 wait_timeout(默认通常为 8 小时,但在部分云数据库上可能只有几分钟),原有连接将被服务端单方面切断。此时应在执行 SQL 前使用 connection.ping(reconnect=True) 激活并重连。
  • 数据类型与脏数据溢出:爬取到的价格、数量字符串中可能包含货币符号或文字(如 "¥128元")。必须在 Python 阶段将其清洗为合法的浮点数或整型,否则强行写入会触发 Data truncation 警告或直接报错导致事务回滚。
  • 主键重复冲突处理:在 INSERT 语句中合理运用 ON DUPLICATE KEY UPDATE 或 INSERT IGNORE,使爬虫支持幂等运行,防止重复抓取时因唯一键冲突终止执行。
SVG 配图 4
图4:Python 爬虫将数据写入 MySQL 的全流程核心要素总结

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表