Python Scrapy框架爬虫项目结构与核心代码实现解析

凌晨两点半
凌晨两点半
发布时间:2026-11-04| 3人浏览过
原创

深入解析Python Scrapy爬虫框架的标准项目结构与核心代码实现,剖析spiders、items、pipelines与settings各层职责与协作逻辑。

一、Scrapy 标准项目目录结构与各模块职责

在终端执行 scrapy startproject <project_name> 后,Scrapy 会自动生成一套规范化的项目目录。理解各文件的分工是编写可维护爬虫的第一步。典型目录结构如下:

myproject/
├── scrapy.cfg                # 项目部署配置文件(标记项目根目录)
└── myproject/               # 项目 Python 模块源码目录
    ├── __init__.py
    ├── items.py              # 数据模型定义文件(定义提取目标字段)
    ├── middlewares.py        # 中间件文件(下载中间件与爬虫中间件钩子)
    ├── pipelines.py          # 管道文件(数据清洗、去重与持久化存储)
    ├── settings.py           # 项目全局设置文件(并发、延迟、请求头与管道注册)
    └── spiders/              # 爬虫逻辑主目录
        ├── __init__.py
        └── quotes_spider.py  # 具体的爬虫实现类

各模块的具体职责划分如下:

  • scrapy.cfg:项目的配置文件,通常位于最外层。它指定了项目的 settings 模块路径以及部署目标(如 Scrapyd 服务端)。当在任意子目录运行 scrapy 命令时,Scrapy 正是通过向上查找该文件来定位项目根目录的。
  • items.py:声明结构化数据的数据契约。通过继承 scrapy.Item 并使用 scrapy.Field() 声明字段,为爬虫产出的散乱数据提供统一约束,防止因字典键名拼写错误导致数据丢失。
  • spiders/ 目录:存放爬虫的核心代码。每个爬虫类需继承自 scrapy.Spider(或其子类),定义初始抓取链接(start_urls)与解析回调方法(parse),负责将 HTML 响应转换为 Item 或后续抓取请求。
  • pipelines.py:负责消费爬虫产出的 Item。当 Spider 通过 yield item 发送数据后,引擎会自动将 Item 顺次投递至已启用的 Pipeline。在此处执行字段清洗、去重验证或写入 MySQL、MongoDB、本地 JSON 文件等持久化操作。
  • middlewares.py:提供拦截并修改 Request 和 Response 的钩子机制。主要包含 DownloaderMiddleware(用于统一设置随机 User-Agent、挂载代理 IP、处理重试)与 SpiderMiddleware(处理爬虫输出)。
  • settings.py:爬虫运行的“总控开关”。在此配置文件中可调节最大并发请求数(CONCURRENT_REQUESTS)、下载延迟(DOWNLOAD_DELAY)、是否遵守 Robots 协议,以及为各个 Pipeline 和 Middleware 分配权重以激活执行。
Python Scrapy框架爬虫项目结构与核心代码实现解析
图 1:Scrapy 项目目录树与各组件功能映射关系

二、核心代码实现:从字段建模到管道入库

下面以抓取经典公开练习站点 quotes.toscrape.com 的名言、作者与标签为例,演示一个完整的 Scrapy 核心代码实现链路。

1. 数据模型定义:items.py

在 items.py 中定义抓取数据的结构字段。使用 scrapy.Field() 占位,为整个项目确立统一的数据字段契约:

# myproject/items.py
import scrapy

class QuoteItem(scrapy.Item):
    """定义名言数据项的结构字段"""
    text = scrapy.Field()    # 名言正文内容
    author = scrapy.Field()  # 作者姓名
    tags = scrapy.Field()    # 标签列表(以逗号拼接或数组形式存储)

2. 爬虫解析逻辑:spiders/quotes_spider.py

在 spiders/ 目录下创建 quotes_spider.py。爬虫类必须指定唯一的 name,并在 parse 方法中使用 CSS 或 XPath 选择器提取页面元素。遇到下一页链接时,通过 response.follow 或 scrapy.Request 生成新的抓取任务:

# myproject/spiders/quotes_spider.py
import scrapy
from myproject.items import QuoteItem

class QuotesSpider(scrapy.Spider):
    name = "quotes"
    allowed_domains = ["quotes.toscrape.com"]
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        """解析页面中的名言区块,并处理分页链接"""
        quote_blocks = response.css("div.quote")

        for block in quote_blocks:
            item = QuoteItem()
            # 提取名言文本,去除首尾符号
            item["text"] = block.css("span.text::text").get(default="").strip("“”")
            # 提取作者
            item["author"] = block.css("small.author::text").get(default="").strip()
            # 提取标签列表
            item["tags"] = block.css("div.tags a.tag::text").getall()

            # 将提取出的数据结构推送到引擎
            yield item

        # 查找下一页链接并生成新的爬取请求
        next_page = response.css("li.next a::attr(href)").get()
        if next_page is not None:
            # response.follow 会自动处理相对路径并绑定 parse 回调
            yield response.follow(next_page, callback=self.parse)
开发注意事项:在 parse 回调中,绝不可写阻塞式的同步网络请求(如 time.sleep 或 requests.get)。Scrapy 运行在单线程的 Twisted 反应器(Reactor)上,任何阻塞操作都会直接卡死整个异步事件循环,导致其他并发任务全部挂起。如需控制爬取频率,应在 settings.py 中配置 DOWNLOAD_DELAY。

3. 数据清洗与存储:pipelines.py

在 pipelines.py 中编写管道类。每个管道需实现 process_item(item, spider) 方法。数据在管道中链式传递,必须在方法末尾返回 item,或者抛出 DropItem 异常中断废弃数据:

# myproject/pipelines.py
import json
from scrapy.exceptions import DropItem

class CleanQuotePipeline:
    """清洗管道:去除多余空白,过滤无效内容"""
    def process_item(self, item, spider):
        text = item.get("text", "")
        if not text:
            raise DropItem(f"发现空名言,丢弃该条数据: {item}")
        
        # 将标签列表转换为逗号分隔的字符串,便于后续存储
        item["tags"] = ", ".join(item.get("tags", []))
        return item

class JsonWriterPipeline:
    """持久化管道:以 JSON Lines 格式写入本地文件"""
    def open_spider(self, spider):
        # 爬虫开启时打开文件句柄
        self.file = open("quotes_cleaned.jsonl", "w", encoding="utf-8")

    def close_spider(self, spider):
        # 爬虫关闭时确保文件写入并关闭句柄
        self.file.close()

    def process_item(self, item, spider):
        line = json.dumps(dict(item), ensure_ascii=False) + "\n"
        self.file.write(line)
        return item

4. 运行策略配置:settings.py

编写完 Pipeline 后,必须在 settings.py 中显式激活。数字键代表优先级(范围 1~1000,数值越小越先执行):

# myproject/settings.py

BOT_NAME = "myproject"
SPIDER_MODULES = ["myproject.spiders"]
NEWSPIDER_MODULE = "myproject.spiders"

# 尊重目标站点的 robots.txt 规则
ROBOTSTXT_OBEY = True

# 设置下载延迟与并发,减轻目标服务器压力
DOWNLOAD_DELAY = 1.0
CONCURRENT_REQUESTS_PER_DOMAIN = 8

# 请求头配置
DEFAULT_REQUEST_HEADERS = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
}

# 激活数据持久化管道及其处理顺序
ITEM_PIPELINES = {
    "myproject.pipelines.CleanQuotePipeline": 300,  # 先清洗
    "myproject.pipelines.JsonWriterPipeline": 400,   # 后入库
}
Python Scrapy框架爬虫项目结构与核心代码实现解析
图 2:Scrapy 核心引擎与组件之间的数据流动与协作原理

三、Scrapy 爬虫项目的构建、运行与调试步骤

完成上述代码后,项目的标准执行流程涵盖环境准备、爬虫调用与产物核对四个环节:

Python Scrapy框架爬虫项目结构与核心代码实现解析
图 3:Scrapy 爬虫项目搭建与调试四步操作路径

步骤 1:创建项目与生成模版

在目标工作目录打开终端,依次执行以下命令:

# 1. 创建项目骨架
scrapy startproject quotes_spider

# 2. 进入项目主目录
cd quotes_spider

# 3. 使用 genspider 快速生成爬虫模版文件
scrapy genspider quotes quotes.toscrape.com

步骤 2:启动爬虫并验证输出

运行 quotes 爬虫并查看实时执行日志:

scrapy crawl quotes

控制台将输出 Scrapy 引擎初始化的核心统计日志(示例输出):

终端示例输出(非实际执行结果)
2026-11-01 16:30:10 [scrapy.utils.log] INFO: Scrapy 2.11.0 started (bot: myproject)
2026-11-01 16:30:10 [scrapy.core.engine] INFO: Spider opened
2026-11-01 16:30:11 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://quotes.toscrape.com/> (referer: None)
2026-11-01 16:30:12 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://quotes.toscrape.com/page/2/>
...
2026-11-01 16:30:22 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
  'item_scraped_count': 100,
  'response_received_count': 10,
  'downloader/request_count': 10,
2026-11-01 16:30:22 [scrapy.core.engine] INFO: Spider closed (finished)

执行完成后,检查项目根目录下的 quotes_cleaned.jsonl,每行为一条清洗后的完整 JSON 记录:

{"text": "The world as we have created it is a process of our thinking.", "author": "Albert Einstein", "tags": "change, deep-thoughts, thinking"}
{"text": "It is our choices, Harry, that show what we truly are, far more than our abilities.", "author": "J.K. Rowling", "tags": "abilities, choices"}

四、关键差异对比:单文件脚本 vs Scrapy 工程化多层架构

在完成相同的数据采集需求时,开发者常在“单文件脚本”(如 requests + BeautifulSoup 编写的单一 .py 脚本)与“Scrapy 工程化架构”之间选择。二者的核心差异并非仅是代码行数的多少,而在于架构设计对系统维护性与扩展性的深层影响。

对比维度 传统单文件脚本模式 Scrapy 标准工程化架构
并发机制 同步阻塞循环;若需并发需手动引入 threading 或 multiprocessing,存在死锁与线程安全问题。 内置 Twisted 异步 Reactor,单线程非阻塞高并发,自动管理事件循环与连接池。
模块耦合度 网络请求、HTML 解析、数据清洗、文件写入强混杂在单一循环体内,牵一发而动全身。 职责严格解耦:Spider 仅负责提取,Pipeline 负责清洗入库,Downloader 负责调度。
去重与调度 需开发者手动维护 URL 集合或 Redis 键,易发生重复爬取或遗漏。 Scheduler 自动对 Request 计算 SHA1 指纹进行去重,支持队列深度/广度优先切换。
异常容错能力 某次请求异常或单条数据缺失可能导致整段脚本崩溃,需层层包裹 try...except。 内置多级容错与重试中间件,单条 Item 异常被 DropItem 拦截,不影响其余任务。
维护与复用 新增爬虫时往往复制粘贴整套请求与入库逻辑,逻辑重复率高。 共享 Pipelines、Middlewares 与 Settings,新增数据源只需编写独立的 Spider 类。
Python Scrapy框架爬虫项目结构与核心代码实现解析
图 4:单文件脚本与 Scrapy 工程化多层架构模式对比总结

为什么严禁在 Spider 内部直接写入数据库?

初学者容易在 spiders/quotes_spider.py 的 parse 方法中直接编写 pymysql.connect() 或写入本地文件。这种做法破坏了 Scrapy 的核心设计理念:

  1. 破坏并发性能:数据库写入或磁盘 I/O 通常是同步阻塞操作。如果在 parse 回调中阻塞数毫秒,Twisted 反应器就无法及时响应其他并发请求的网络响应,吞吐量将急剧下跌。
  2. 阻碍多目标持久化:若将来需要将数据同时输出到 Elasticsearch、Kafka 或进行质量监控,直接写死在 Spider 内部会导致逻辑膨胀失控;而通过配置 ITEM_PIPELINES,只需追加一个独立 Pipeline 即可无侵入式接入。

五、核心要点总结与开发建议

掌握 Scrapy 框架的核心,在于牢记各个组件在数据生命周期中的位置与契约:

  • 以数据为核心推进:先在 items.py 中确立数据契约,再进入 spiders/ 编写提取规则,最后由 pipelines.py 实施持久化。
  • 善用 yield 机制:Spider 是一个生成器,通过 yield scrapy.Request 实现链式翻页与子页面下钻,通过 yield item 将成果交付管道,保持方法纯粹。
  • 全局参数精细化治理:爬虫的礼貌性(下载延迟、并发数)、安全性(User-Agent 轮换、代理中间件)与存储管道的启用顺序,均集中在 settings.py 中收敛配置,避免硬编码。
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。