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(分配/处理)→ 目标环节

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  # 爬够1000页自动停
CLOSESPIDER_ITEMCOUNT = 10000  # 爬够10000条数据自动停
"""

Scheduler(任务调度管家)

Scheduler是“仓库管理员+优先级经理”,专门管理待爬的Requests队列,避免重复爬取、按优先级分配任务。

核心功能

  1. 去重:默认用RFPDupeFilter生成请求指纹(哈希值),判断是否爬过
  2. 排队:支持FIFO/LIFO/优先级队列(默认FIFO)
  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.url import urljoin_rfc
from scrapy.utils.request import request_fingerprint

class IgnoreTimestampDupeFilter(RFPDupeFilter):
    def request_fingerprint(self, request):
        # 复制一个request,移除时间戳参数
        new_request = request.replace(url=request.url.split('?')[0])
        return request_fingerprint(new_request)

Downloader(内容搬运工)

Downloader是“高速快递员”,负责发送HTTP请求、接收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响应、生成新Requests、生成Items。

最常用的2种Spider

  1. Basic 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(),
            }

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

Pipeline(数据加工厂)

Pipeline是“质检+仓储流水线”,负责处理Spider生成的Items:清洗数据、验证必填项、去重(比Scheduler的URL去重更细)、存储到数据库/文件。

Pipeline的生命周期

每个Pipeline必须实现以下1-2个方法(其他可选):

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

配置顺序

# settings.py 中按数字从小到大依次执行(300先验证,500后存储)
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队列空 → 无新Requests生成 → Engine关闭所有组件 → 结束爬虫

优化小锦囊

性能优化

  1. 合理设并发:总并发16-64,单域名2-8,别太高容易被封
  2. 必开自动限速AUTOTHROTTLE_ENABLED = True
  3. 复用中间件逻辑:别把所有反爬代码写在Spider里,用中间件更方便复用

内存优化

  1. 及时丢弃无效Item:用raise DropItem
  2. 限制爬取时长/页数/条数:比如CLOSESPIDER_TIMEOUT = 3600

常见问题快答

Q1: Spider怎么传参数给Pipeline?

A: 可以用spider.settings.get()或者直接spider.custom_param(在Spider里定义属性)。

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

A: 在settings.py里设置JOBDIR = 'crawls/xxx/',启动爬虫后会自动保存状态,中断后再次启动同一个命令就能续爬!

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

A: 在生成详情页Request时加priority=5(列表页默认0或1,数字越大优先级越高)。


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