Scrapy框架数据存储完全指南(实操为主,易落地)

你想掌握Scrapy框架的数据存储方法,核心是围绕Item数据模型和**Pipeline(数据管道)**展开------Scrapy将爬取到的数据先封装到Item中,再通过Pipeline统一处理存储,支持本地文件、关系型数据库、非关系型数据库等多种存储方式,以下是详细实操教程,无冗余理论,可直接复刻运行。

一、前置基础:数据存储的核心流程

  1. 定义Item:在items.py中声明需要存储的字段,规范数据格式(避免杂乱数据);
  2. 解析数据封装到Item:在爬虫文件中,将解析到的数据存入自定义Item对象并yield
  3. 启用Pipeline:在settings.py中配置启用对应的管道(设置优先级,数字越小优先级越高);
  4. 实现存储逻辑:在pipelines.py中编写具体的存储代码(文件/数据库等);
  5. 运行爬虫:自动触发Pipeline,完成数据存储。

必备前置准备(快速搭建测试环境)

  1. 已有Scrapy项目(若无,执行scrapy startproject data_storage_spider创建);
  2. 简单爬虫示例(用于生成测试数据,后续所有存储方式均基于此):
  • 爬虫文件spiders/test_spider.py
python 复制代码
import scrapy
from data_storage_spider.items import DataStorageSpiderItem

class TestSpider(scrapy.Spider):
    name = "test_spider"
    # 测试用静态页面,无需处理动态渲染
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        # 解析名言数据
        quote_items = response.xpath("//div[@class='quote']")
        for item in quote_items:
            # 实例化自定义Item
            quote_data = DataStorageSpiderItem()
            # 封装数据(字段与items.py中定义一致)
            quote_data['content'] = item.xpath(".//span[@class='text']/text()").extract_first() or ""
            quote_data['author'] = item.xpath(".//small[@class='author']/text()").extract_first() or ""
            quote_data['tags'] = ",".join(item.xpath(".//div[@class='tags']/a/text()").extract()) or ""
            
            # 提交数据到Pipeline(核心:yield Item对象)
            yield quote_data
  • 定义Itemitems.py
python 复制代码
import scrapy

class DataStorageSpiderItem(scrapy.Item):
    # 定义需要存储的字段,scrapy.Field()仅用于标记,无额外逻辑
    content = scrapy.Field()  # 名言内容
    author = scrapy.Field()   # 作者
    tags = scrapy.Field()     # 标签(用逗号拼接为字符串,方便存储)

二、方式1:本地文件存储(CSV/JSON/JSON Lines,最常用)

本地文件存储无需额外安装依赖,适合小规模数据、快速验证结果,Scrapy提供两种实现方式:内置命令直接导出 (快速便捷)、Pipeline自定义实现(灵活可控,支持复杂逻辑)。

1. 快速实现:Scrapy内置命令导出(无需编写Pipeline)

直接在终端运行爬虫时,通过-o参数指定文件路径和格式,自动生成文件,支持csvjsonjl(JSON Lines)三种常用格式。

(1)导出为CSV文件(推荐,易用于Excel分析)

bash 复制代码
# 进入项目目录后执行
scrapy crawl test_spider -o quotes.csv -s FEED_EXPORT_ENCODING=utf_8_sig
  • 关键参数说明:
    • -o quotes.csv:指定导出为CSV文件,文件名quotes.csv
    • -s FEED_EXPORT_ENCODING=utf_8_sig:解决中文乱码问题(核心参数,必加);
  • 结果:项目根目录生成quotes.csv,可用Excel直接打开,数据格式规整。

(2)导出为JSON文件

bash 复制代码
scrapy crawl test_spider -o quotes.json -s FEED_EXPORT_ENCODING=utf_8_sig
  • 特点:数据以数组格式存储,适合后续JSON解析处理,缺点是大文件加载耗时久。

(3)导出为JSON Lines文件(推荐,大文件友好)

bash 复制代码
scrapy crawl test_spider -o quotes.jl -s FEED_EXPORT_ENCODING=utf_8_sig
  • 特点:每行一条JSON数据,无需加载整个文件即可解析,适合大规模本地数据存储,兼容性更强。

2. 灵活实现:Pipeline自定义文件存储(支持复杂逻辑)

内置命令无法满足自定义需求(如:按日期拆分文件、过滤无效数据、追加写入),此时需通过Pipeline实现,以CSV文件为例(JSON格式同理)。

(1)编写Pipeline存储逻辑(pipelines.py

python 复制代码
import csv
import os

class CsvFileStoragePipeline:
    """自定义CSV文件存储管道"""
    def __init__(self):
        # 1. 初始化文件对象和CSV写入器
        self.file = None
        self.writer = None
        # CSV文件路径和表头
        self.csv_path = "custom_quotes.csv"
        self.fieldnames = ["content", "author", "tags"]
    
    def open_spider(self, spider):
        """爬虫启动时执行(仅执行一次),用于打开文件、初始化写入器"""
        # 追加写入模式(a):避免覆盖已有数据;newline="":避免CSV出现空行
        self.file = open(self.csv_path, "a", newline="", encoding="utf_8_sig")
        # 检查文件是否为空,为空则写入表头
        if os.path.getsize(self.csv_path) == 0:
            self.writer = csv.DictWriter(self.file, fieldnames=self.fieldnames)
            self.writer.writeheader()
        else:
            self.writer = csv.DictWriter(self.file, fieldnames=self.fieldnames)
    
    def process_item(self, item, spider):
        """核心:处理每个Item数据(爬虫提交一个Item,执行一次)"""
        # 过滤无效数据(自定义逻辑:排除空内容的记录)
        if not item.get("content") or item.get("content").strip() == "":
            spider.logger.warning("无效数据:内容为空,跳过存储")
            return item
        
        # 将Item转换为字典,写入CSV文件
        self.writer.writerow(dict(item))
        spider.logger.info(f"数据已写入CSV:{item.get('author')} - {item.get('content')[:20]}...")
        return item
    
    def close_spider(self, spider):
        """爬虫关闭时执行(仅执行一次),用于关闭文件,释放资源"""
        if self.file:
            self.file.close()
            spider.logger.info(f"CSV文件存储完成,文件路径:{self.csv_path}")

(2)启用Pipeline(settings.py

找到ITEM_PIPELINES配置,取消注释并添加自定义管道(优先级300,可根据需求调整):

python 复制代码
ITEM_PIPELINES = {
   'data_storage_spider.pipelines.CsvFileStoragePipeline': 300,
}

(3)运行爬虫验证结果

bash 复制代码
scrapy crawl test_spider
  • 结果:项目根目录生成custom_quotes.csv,无中文乱码,已过滤空内容数据,支持追加写入(重复运行爬虫,数据不会覆盖,而是新增到文件末尾)。

三、方式2:关系型数据库存储(以MySQL为例,生产环境常用)

适合大规模结构化数据存储,支持数据查询、更新、关联分析,核心依赖pymysql库,步骤如下:

1. 安装依赖库

bash 复制代码
pip install pymysql

2. 提前创建MySQL数据库和表

  • 创建数据库scrapy_data(字符集utf8mb4,支持所有中文和特殊字符);
  • 创建表quotes(字段与Item对应,结构如下):
sql 复制代码
CREATE DATABASE IF NOT EXISTS scrapy_data DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE scrapy_data;

CREATE TABLE IF NOT EXISTS quotes (
    id INT PRIMARY KEY AUTO_INCREMENT COMMENT '自增主键',
    content TEXT NOT NULL COMMENT '名言内容',
    author VARCHAR(100) NOT NULL COMMENT '作者',
    tags VARCHAR(255) DEFAULT '' COMMENT '标签',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '存储时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '名言数据表';

3. 编写MySQL存储Pipeline(pipelines.py

python 复制代码
import pymysql

class MysqlStoragePipeline:
    """MySQL数据库存储管道"""
    def __init__(self):
        # 数据库连接配置(根据自身环境修改)
        self.host = "localhost"
        self.port = 3306
        self.user = "root"
        self.password = "你的MySQL密码"
        self.db_name = "scrapy_data"
        self.charset = "utf8mb4"
        
        # 初始化连接和游标对象
        self.conn = None
        self.cursor = None
    
    def open_spider(self, spider):
        """爬虫启动时,建立MySQL连接"""
        try:
            self.conn = pymysql.connect(
                host=self.host,
                port=self.port,
                user=self.user,
                password=self.password,
                db=self.db_name,
                charset=self.charset
            )
            self.cursor = self.conn.cursor()
            spider.logger.info("MySQL数据库连接成功")
        except Exception as e:
            spider.logger.error(f"MySQL数据库连接失败:{e}")
            raise e
    
    def process_item(self, item, spider):
        """核心:将Item数据插入MySQL数据库"""
        if not item.get("content"):
            return item
        
        # 1. 定义SQL插入语句(使用占位符%s,避免SQL注入)
        insert_sql = """
        INSERT INTO quotes (content, author, tags)
        VALUES (%s, %s, %s)
        """
        
        # 2. 提取Item数据,组装为参数元组(与SQL占位符顺序对应)
        item_data = (
            item.get("content"),
            item.get("author"),
            item.get("tags")
        )
        
        try:
            # 3. 执行SQL语句
            self.cursor.execute(insert_sql, item_data)
            # 4. 提交事务(关键:不提交则数据不会写入数据库)
            self.conn.commit()
            spider.logger.info(f"数据已插入MySQL:{item.get('author')}")
        except Exception as e:
            # 5. 出错时回滚事务,避免数据混乱
            self.conn.rollback()
            spider.logger.error(f"数据插入MySQL失败:{e},数据:{item_data}")
        
        return item
    
    def close_spider(self, spider):
        """爬虫关闭时,关闭游标和连接"""
        if self.cursor:
            self.cursor.close()
        if self.conn:
            self.conn.close()
        spider.logger.info("MySQL数据库连接已关闭")

4. 启用MySQL Pipeline(settings.py

python 复制代码
ITEM_PIPELINES = {
   # 可同时启用多个Pipeline,分别存储为CSV和MySQL(优先级300<400,CSV先执行)
   'data_storage_spider.pipelines.CsvFileStoragePipeline': 300,
   'data_storage_spider.pipelines.MysqlStoragePipeline': 400,
}

5. 运行爬虫验证结果

bash 复制代码
scrapy crawl test_spider
  • 验证:登录MySQL,查询scrapy_data.quotes表,可看到已成功插入数据,无乱码,字段对应完整。

四、方式3:非关系型数据库存储(以MongoDB为例,灵活存储非结构化数据)

适合存储非结构化/半结构化数据(如字段不固定的爬取数据),无需提前定义表结构,核心依赖pymongo库,步骤如下:

1. 安装依赖库

bash 复制代码
pip install pymongo

2. 提前启动MongoDB服务

  • 本地MongoDB:启动mongod服务(默认端口27017,无需手动创建数据库和集合,插入数据时自动创建);
  • 远程MongoDB:准备好连接地址、用户名、密码。

3. 编写MongoDB存储Pipeline(pipelines.py

python 复制代码
import pymongo

class MongodbStoragePipeline:
    """MongoDB数据库存储管道"""
    def __init__(self):
        # MongoDB连接配置(本地无密码,远程需添加username、password、authSource)
        self.host = "localhost"
        self.port = 27017
        self.db_name = "scrapy_data"
        self.collection_name = "quotes"  # 集合(类似MySQL的表)
        
        # 初始化数据库连接和集合对象
        self.client = None
        self.db = None
        self.collection = None
    
    def open_spider(self, spider):
        """爬虫启动时,建立MongoDB连接"""
        try:
            # 建立连接(本地无密码)
            self.client = pymongo.MongoClient(host=self.host, port=self.port)
            # 选择数据库(不存在则自动创建)
            self.db = self.client[self.db_name]
            # 选择集合(不存在则自动创建)
            self.collection = self.db[self.collection_name]
            spider.logger.info("MongoDB数据库连接成功")
        except Exception as e:
            spider.logger.error(f"MongoDB数据库连接失败:{e}")
            raise e
    
    def process_item(self, item, spider):
        """核心:将Item数据插入MongoDB(转换为字典即可)"""
        if not item.get("content"):
            return item
        
        # 1. 将Item转换为字典(Scrapy Item对象可直接强转为dict)
        item_dict = dict(item)
        
        try:
            # 2. 插入数据(insert_one:插入单条数据;insert_many:插入多条数据)
            self.collection.insert_one(item_dict)
            spider.logger.info(f"数据已插入MongoDB:{item_dict.get('author')}")
        except Exception as e:
            spider.logger.error(f"数据插入MongoDB失败:{e},数据:{item_dict}")
        
        return item
    
    def close_spider(self, spider):
        """爬虫关闭时,关闭MongoDB连接"""
        if self.client:
            self.client.close()
            spider.logger.info("MongoDB数据库连接已关闭")

4. 启用MongoDB Pipeline(settings.py

python 复制代码
ITEM_PIPELINES = {
   'data_storage_spider.pipelines.CsvFileStoragePipeline': 300,
   'data_storage_spider.pipelines.MysqlStoragePipeline': 400,
   'data_storage_spider.pipelines.MongodbStoragePipeline': 500,
}

5. 运行爬虫验证结果

bash 复制代码
scrapy crawl test_spider
  • 验证:使用mongo终端连接,执行use scrapy_data; db.quotes.find().pretty();,可看到已成功插入数据,字段完整。

五、核心注意事项与实操技巧

  1. Pipeline优先级ITEM_PIPELINES中的数字(1-1000)越小,优先级越高,先执行的Pipeline处理后的Item会传递给后续Pipeline;
  2. 数据去重
    • MySQL:可给content字段添加唯一索引(ALTER TABLE quotes ADD UNIQUE INDEX uk_content (content(255));),避免重复插入;
    • MongoDB:使用update_one(),设置upsert=True(存在则更新,不存在则插入),基于唯一字段去重;
  3. 异常处理与事务
    • 数据库操作必须添加try-except,避免单个数据插入失败导致整个爬虫崩溃;
    • MySQL需手动提交事务(conn.commit()),出错时回滚(conn.rollback());MongoDB无需手动提交,默认自动写入;
  4. 中文乱码
    • 本地文件:必须使用encoding="utf_8_sig"(CSV/JSON);
    • MySQL:数据库、表、字段均使用utf8mb4字符集;
    • MongoDB:默认支持UTF-8,无需额外配置;
  5. 资源释放open_spider()建立连接,close_spider()关闭连接,避免资源泄露(尤其长期运行的分布式爬虫);
  6. 大规模数据优化
    • 本地文件:避免一次性写入大文件,可按批次拆分;
    • 数据库:使用批量插入(MySQLexecutemany()、MongoDBinsert_many()),减少数据库交互次数,提升效率。
相关推荐
智算菩萨5 小时前
Python可以做哪些小游戏——基于Python 3.13最新特性的游戏开发全指南(15万字超长文章,强烈建议收藏阅读)
python·pygame
智航GIS5 小时前
9.1 多线程入门
java·开发语言·python
nvd115 小时前
FastMCP 开发指南: 5分钟入门
人工智能·python
weixin_433179336 小时前
Python - word jumble游戏
开发语言·python
Iridescent11216 小时前
Iridescent:Day48
python
BBB努力学习程序设计7 小时前
Python迭代器与生成器:优雅的惰性计算艺术
python
BBB努力学习程序设计7 小时前
Python描述符协议:属性访问的底层魔法
python
qq_317620317 小时前
第00章-Python学习大纲
python·python入门
Hello.Reader7 小时前
Table & SQL API 配置从“默认可用”到“针对场景调优”的一套方法论
数据库·python·sql
BoBoZz197 小时前
VTKWithNumpy使用 NumPy 数组来创建3D体渲染所需要的数据
python·vtk·图形渲染·图形处理