Day 077 — 图解:请求调度与数据管道
1. Scrapy 数据流全景图
┌──────────────────────────────────────┐
│ Spider │
│ (解析响应,产出 Request 和 Item) │
└──────┬──────────────┬────────────────┘
│ │
Request │ │ Item
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Scheduler │ │ Pipeline │
│ (调度器) │ │ (管道) │
└───────┬───────┘ └───────┬───────┘
│ │
┌────────────┤ │
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌──────────┐ ┌──────────────┐
│ DupeFilter │ │ Queue │ │ 数据处理链 │
│ (去重过滤) │ │ (请求队列)│ │ 清洗→验证→存储│
└─────────────┘ └──────────┘ └──────────────┘
│ │
└────────────┤
▼
┌───────────────┐
│ Downloader │
│ (下载器) │
└───────┬───────┘
│
▼
┌───────────────┐
│ Middleware │
│ (中间件) │
└───────────────┘
2. 调度器内部机制
┌─────────────────────────────────────────────────────────────┐
│ Scrapy Scheduler │
│ │
│ 新请求进入 │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ DupeFilter │── 指纹已存在 ──→ ⛔ 丢弃 │
│ │ (指纹检查) │ │
│ └──────┬──────┘ │
│ │ 指纹不存在 │
│ ▼ │
│ ┌─────────────┐ │
│ │ Priority │ │
│ │ Queue │ priority=10 ┐ │
│ │ (优先级堆) │ priority=5 ├→ 出队时取最高优先级 │
│ │ │ priority=0 ┘ │
│ └──────┬──────┘ │
│ │ │
│ ▼ │
│ 下一个待爬取请求 │
└─────────────────────────────────────────────────────────────┘
3. Pipeline 执行流程
Item 产出
│
▼
┌─────────────────────────────────────────────────────────┐
│ Pipeline 100: CleanPipeline │
│ • 去除空白字符 │
│ • 标准化价格格式 │
│ • 添加时间戳 │
│ 返回处理后的 Item ──→ ✅ │
│ 无效数据 ──→ raise DropItem ──→ ⛔ 丢弃 │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Pipeline 200: ValidatePipeline │
│ • 必填字段检查 │
│ • 类型校验 │
│ • 业务规则校验 │
│ 返回处理后的 Item ──→ ✅ │
│ 不合规数据 ──→ raise DropItem ──→ ⛔ 丢弃 │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Pipeline 300: StorePipeline │
│ • 写入数据库 │
│ • 或导出为文件 │
│ 返回处理后的 Item ──→ ✅ │
└─────────────────────────────────────────────────────────┘
4. 去重指纹生成流程
Request(url, method, body)
│
▼
┌──────────────────────────────┐
│ 1. 提取 URL 参数 │
│ url = "https://a.com/p=1&t=2"│
│ params = {p: [1], t: [2]} │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 2. 移除忽略参数(可选) │
│ remove(params, 't') │
│ params = {p: [1]} │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 3. 排序并重建 URL │
│ clean_url = "https://a.com/p=1"│
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 4. 生成哈希指纹 │
│ fp = sha1(clean_url + method + body)│
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ 5. 检查指纹集合 │
│ fp in seen? │
│ ├─ No → 允许,记录指纹 │
│ └─ Yes → 过滤掉 │
└──────────────────────────────┘
5. 断点续爬机制
第一次运行 中断后恢复
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 开始爬取 │ │ 读取 JOBDIR │
│ │ │ │
│ 处理 50% │ 进程崩溃 │ 恢复队列状态 │
│ │ │ ─────────────→ │ 恢复去重集合 │
│ ▼ │ │ │ │
│ 保存状态 │ │ ▼ │
│ 到 JOBDIR │ │ 继续爬取 │
│ │ │ 剩余 50% │
└─────────────┘ └─────────────┘
JOBDIR 保存的内容:
├── queue/ # 请求队列(未处理的请求)
├── dupefilter/ # 去重指纹集合
└── requests.queue # 调度器状态