Scrapy五大核心组件详解 - 五脏六腑怎么协同干活?

大家好呀,我是道满PythonAI!上一篇我们搞懂了为什么选Scrapy(异步、高并发、扩展性强),今天直接把它拆开来学——核心5件套不神秘,看完你就明白 Scrapy 怎么能“快、稳、巧”地爬数据!

📂 所属阶段:第一阶段 — 初出茅庐(框架核心篇)
🔗 相关章节:为什么选择 Scrapy? · 创建你的首个工程


目录


1分钟看全局架构

Scrapy 采用事件驱动异步架构,5 个核心组件各司其职,Engine 充当“总指挥”串联所有环节,自动形成一条数据流水线:

flowchart LR
    A[Spider<br>解析逻辑师] -->|Requests| B[Engine<br>核心指挥家]
    B -->|排队Requests| C[Scheduler<br>任务调度管家]
    C -->|取优先级Requests| B
    B -->|发Requests| D[Downloader<br>内容搬运工]
    D -->|HTTP请求| E[目标网站]
    E -->|HTTP响应| D
    D -->|Responses| B
    B -->|Responses| A
    A -->|新Requests/Items| B
    B -->|Items| F[Pipeline<br>数据加工厂]
    F --> G[(数据存储)]

极简数据流向

Spider(要爬的URL / 解析出的数据)→ Engine(分配、协调)→ 目标组件

你只需要在 Spider 里写“解析什么、怎么跟新链接”,剩下的调度、下载、存库都由框架搞定。


Engine(核心指挥家)

Engine 是 Scrapy 的“大脑”,它不干具体的爬取活儿,但决定所有组件“什么时候、做什么、交给谁”。

核心职责

  1. 事件循环驱动:利用 Twisted 异步库接管整个流程的执行节奏
  2. 组件生命周期管理:启动、运行、暂停、停止爬虫
  3. 信号分发与处理:协调组件之间的状态通知
  4. 数据流转监控:保证 Requests / Responses / Items 在组件间正确传递

常用生命周期信号

# 在中间件或爬虫中常用的信号(不需要写太多代码,注册即可)
from scrapy import signals

# spider_opened:爬虫刚启动(可用来初始化数据库连接)
# spider_closed:爬虫停止前(可关闭连接、打印统计信息)
# response_received:刚拿到 HTTP 响应(适合加一些通用监控)
# item_scraped:Item 经过所有 Pipeline 成功生成后触发

配置注意

# settings.py 中和 Engine 相关的核心开关
"""
# 爬虫空闲多久后自动停止(0 表示不自动停,生产环境建议设)
CLOSESPIDER_TIMEOUT = 3600      # 1小时后自动停止

# 爬够指定页数后自动停止
CLOSESPIDER_PAGECOUNT = 1000

# 爬够指定条数后自动停止
CLOSESPIDER_ITEMCOUNT = 10000
"""

这些参数能帮你控制抓取规模和资源消耗,避免无限运行。


Scheduler(任务调度管家)

Scheduler 是“仓库管理员 + 优先级经理”,专门管理待爬的 Requests 队列,做到去重、排队、续爬三大功能。

核心功能

  1. 去重:默认使用 RFPDupeFilter 生成请求指纹(哈希值),判断是否爬过
  2. 排队:支持 FIFO / LIFO / 优先级队列,默认先进先出
  3. 持久化:设置 JOBDIR 后,中断的任务可以从断点续爬

配置与技巧

# settings.py 里的调度相关配置
"""
# 去重器(想自定义去重逻辑?改这个类)
DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'

# 持久化续爬目录(生成后别随便删,中断后重启命令就能续爬)
JOBDIR = 'crawls/my_spider_20260410/'

# 优先级队列(数字越大优先级越高,默认0)
# 比如可以把详情页优先级设为5,列表页设为1
SCHEDULER_PRIORITY_QUEUE = 'scrapy.pqueues.ScrapyPriorityQueue'
"""

自定义去重(简单版)

# 只需要继承默认去重器,重写指纹生成方法
# 例如:忽略 URL 中的时间戳参数,避免重复爬同一个页面
from scrapy.dupefilters import RFPDupeFilter
from scrapy.utils.request import request_fingerprint

class IgnoreTimestampDupeFilter(RFPDupeFilter):
    def request_fingerprint(self, request):
        # 拷贝一个 request,移除 URL 里的查询参数(如时间戳)
        new_request = request.replace(url=request.url.split('?')[0])
        return request_fingerprint(new_request)

Downloader(内容搬运工)

Downloader 是“高速快递员”,负责发送 HTTP 请求、接收响应,靠着连接复用、自动限速和下载器中间件,稳稳地把网页搬回来。

核心子系统

  1. 下载器中间件(Downloader Middleware):拦截 Requests / Responses,可以修改 User-Agent、设置代理、处理重定向和重试
  2. 连接池:复用 TCP 连接,减少三次握手开销
  3. 自动限速器(AutoThrottle):根据目标网站的响应速度动态调整请求延迟

生产级配置

# settings.py 中的反爬与性能核心配置
"""
# 并发控制(核心!别一次开太高,容易被封)
CONCURRENT_REQUESTS = 16               # 全局并发请求数
CONCURRENT_REQUESTS_PER_DOMAIN = 4     # 单域名并发数(建议2~8)

# 延迟控制(配合自动限速更稳)
DOWNLOAD_DELAY = 1                     # 基础固定延迟1秒
RANDOMIZE_DOWNLOAD_DELAY = 0.5         # 在±50%范围内随机浮动
AUTOTHROTTLE_ENABLED = True            # 强烈建议开启自动限速
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0  # 目标域名并发利用率
AUTOTHROTTLE_MAX_DELAY = 10            # 最大延迟不超过10秒

# 重试控制
RETRY_TIMES = 3                        # 最多重试3次
RETRY_HTTP_CODES = [500, 502, 503, 408, 429]  # 仅重试这些状态码

# 超时控制
DOWNLOAD_TIMEOUT = 180                 # 单次请求最长等待180秒
"""

Spiders(解析逻辑师)

Spiders 是唯一需要你写业务逻辑的地方!在这里定义起始 URL,解析 HTTP 响应,产出新请求和 Item 数据。

最常用的两种 Spider

  1. scrapy.Spider:灵活轻量,适合定制化提取逻辑
  2. CrawlSpider:配合 Rule + LinkExtractor 可自动发现链接,适合全站爬取

Basic Spider 最简示例

import scrapy

class DoubanTop250Spider(scrapy.Spider):
    name = "douban_top250"                  # 必须唯一!运行爬虫时用到的名字
    allowed_domains = ["movie.douban.com"]  # 限制只爬取该域名下的页面
    start_urls = ["https://movie.douban.com/top250?start=0"]

    def parse(self, response):
        # 解析当前页的电影信息
        for movie in response.css("ol.grid_view li"):
            yield {
                "title": movie.css("span.title::text").get(),
                "rating": movie.css("span.rating_num::text").get(),
                "quote": movie.css("span.inq::text").get(),
            }

        # 提取下一页链接,follow() 会自动拼接相对 URL 并回调 self.parse
        next_page = response.css("div.paginator a.next::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

要点:yield 新请求就交给 Engine 继续调度,yield 字典则会被转换成 Item 并经过 Pipeline 管道。


Pipeline(数据加工厂)

Pipeline 是“质检 + 仓储流水线”,对 Spider 产出的 Items 进行清洗、验证、去重,然后安全地存到数据库或文件。

Pipeline 的生命周期

每个 Pipeline 至少要实现以下两个方法之一(更多可选):

  1. open_spider(self, spider):爬虫启动时执行(如初始化 MySQL 连接)
  2. process_item(self, item, spider)必写! 处理每个 Item,返回 Item(或抛出 DropItem 丢弃)
  3. close_spider(self, spider):爬虫停止时执行(如关闭连接)

执行顺序的配置

# settings.py 中按照数字从小到大依次执行(先验证,再清洗,最后存储)
ITEM_PIPELINES = {
    'myproject.pipelines.ValidationPipeline': 300,
    'myproject.pipelines.CleaningPipeline': 400,
    'myproject.pipelines.MongoDBPipeline': 500,
}

完整协同工作流(必看!)

7 步走通整个流程

  1. 初始化
    启动爬虫 → Engine 加载 Spider → Scheduler 初始化队列 → Downloader 准备连接池

  2. 发起起始请求
    Spider 生成 start_urls → Engine 转发给 Scheduler → Scheduler 去重后入队

  3. 取请求
    Engine 从 Scheduler 取优先级最高的 Request → 转发给 Downloader

  4. 下载
    Downloader 发送 HTTP 请求 → 拿到响应 → 交给 Engine

  5. 解析
    Engine 将响应发给 Spider 对应的 callback → Spider 解析出新 RequestsItems

  6. 循环与存储

    • 新 Requests → Engine → Scheduler → 重复步骤 3‑5
    • Items → Engine → 按顺序执行所有 Pipeline → 存入目标位置
  7. 停止
    Scheduler 队列为空且没有新的 Request 生成 → Engine 关闭所有组件 → 爬虫结束


优化小锦囊

性能优化

  1. 合理设置并发:全局 16~64,单域名 2~8,别太高以防被封
  2. 必开自动限速AUTOTHROTTLE_ENABLED = True,让框架自适应
  3. 复用中间件逻辑:不要把反爬代码全堆在 Spider 里,抽到中间件更便于维护

内存优化

  1. 及时丢弃无效 Item:在 Pipeline 中不满足条件就 raise DropItem
  2. 限制爬取规模:用 CLOSESPIDER_TIMEOUTCLOSESPIDER_PAGECOUNTCLOSESPIDER_ITEMCOUNT 控制

常见问题快答

Q1: Spider 怎么传参数给 Pipeline?

A: 可以直接用 spider.settings.get(),或在 Spider 里定义自定义属性(如 spider.custom_param),Pipeline 里通过 spider 对象访问。

Q2: 怎么续爬中断的任务?

A: 在 settings.py 中设置 JOBDIR = 'crawls/xxx/',爬虫启动后会保存中间状态,中断后使用相同命令重新启动即可续爬!

Q3: 怎么让详情页比列表页优先爬取?

A: 生成详情页 Request 时添加 priority=5(默认列表页优先级为 0 或 1,数字越大越优先),调度器会按优先级出队。


🏷️ 标签云: Scrapy 核心组件 Engine Scheduler Downloader Spiders Pipeline 爬虫架构 数据采集