kp-018

Scrapy 架构与核心组件

进阶 ≈ 30 分钟 Scrapy框架SpiderPipelineMiddleware

前置知识

本文基于模型知识整理,建议核对官方文档(见 参考资料)。

一句话定义

Scrapy 是异步采集框架,把「调度、下载、解析、清洗、存储」拆成职责分离的组件;理解其数据流就能把脚本级经验平移成工程级项目。

为什么重要

脚本在三类需求前必然崩塌:数千页面的调度、字段统一清洗、失败任务的追踪。Scrapy 用成熟组件解决了这三件事,其「Spider 管解析、Pipeline 管落地」的分层思想也是一切采集工程的设计范本。

前置知识

kp-006、kp-010;对「框架 = 组件 + 数据流」有直觉。

核心概念

  • Engine:中枢,协调所有组件间的数据流。
  • Scheduler:请求队列(内存/持久化),决定下一个发谁。
  • Downloader:执行 HTTP 请求,返回 Response。
  • Spider:业务核心,定义「从哪开始、怎么解析、产出什么」。
  • Item Pipeline:对抽取结果做清洗、去重、存储的流水线。
  • Downloader / Spider Middleware:请求发出前、响应进入 Spider 前的统一拦截层(改头、重试、UA 轮换都挂这里)。

原理与机制

一次请求的完整数据流(记熟它,报错时才能定位在哪一环):

Spider(start_requests)
  → Engine → Scheduler(入队)
  → Engine → Downloader(经下载中间件) → HTTP
  ← Response(经下载中间件) → Engine → Spider.parse
  parse 产出: Item → Item Pipelines(1..N) / 新 Request → Scheduler

最小项目骨架:

# spiders/books.py
import scrapy

class BooksSpider(scrapy.Spider):
    name = "books"
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):                     # 列表页: 产出详情请求
        for href in response.css("article.product_pod h3 a::attr(href)"):
            yield response.follow(href, callback=self.parse_detail)
        nxt = response.css("li.next a::attr(href)").get()
        if nxt:
            yield response.follow(nxt)             # 翻页也走调度器

    def parse_detail(self, response):              # 详情页: 产出 Item
        yield {
            "title": response.css("h1::text").get(),
            "price": response.css("p.price_color::text").get(),
        }
# pipelines.py —— 职责: 清洗与落库, 绝不在 Spider 里做
class CleanPipeline:
    def process_item(self, item, spider):
        item["price"] = float(item["price"].replace("£", ""))
        return item                                # 返回才继续流入下一 Pipeline

settings 三件套(礼貌抓取的框架化开关,对应 kp-017):ROBOTSTXT_OBEY=True、AUTOTHROTTLE_ENABLED=True、CONCURRENT_REQUESTS_PER_DOMAIN=1。

实例或案例

调试选择器不必整跑 Spider:scrapy shell "https://books.toscrape.com/" 进入交互环境,直接 response.css(...) 试表达式——Scrapy 的开发节奏是 shell 里调通选择器,再抄回 Spider,效率数倍于 print 调试。

常见误区

  • 误区一:在 Spider 里写数据库写入。 职责错位导致无法复用清洗逻辑;存储/清洗/去重一律放 Pipeline。
  • 误区二:把所有请求当回调串。 翻页与详情请求应 yield 回调度器统一排队限速,而不是在回调里自己循环发。
  • 误区三:忽略框架默认值。 ROBOTSTXT_OBEY 默认开启却被随手关掉;CONCURRENT_REQUESTS 调高前先想 kp-004。

自测题

  1. 一条请求在 Scrapy 里经过哪些组件?

答:Spider 产出 → Engine → Scheduler 排队 → Downloader(过下载中间件)→ Response → Spider 回调解析 → Item 入 Pipeline / 新请求回 Scheduler。

  1. Item 与 Pipeline 的分工?

答:Spider 只负责抽取成结构化 Item;清洗、去重、校验、存储在 Pipeline 串联完成。

  1. 下载中间件的典型用途?

答:统一改请求头/UA 轮换、代理注入、响应级重试与异常转换。

与其他知识点的关系

kp-017 的限速被 AUTOTHROTTLE 框架化;kp-021 的去重由调度器内建;kp-022 的 scrapy-redis 把 Scheduler 换成共享 Redis 队列即成分布式。

延伸阅读

Scrapy 官方文档「Architecture overview」(数据流图务必读);Tutorial 章节。

相关知识点

学习进度