kp-022

分布式爬虫与任务队列

进阶 ≈ 25 分钟 分布式Redis消息队列scrapy-redis水平扩展

前置知识

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

一句话定义

分布式爬虫 = 多个 worker 节点从一个共享队列取任务;换来的是水平扩展的吞吐、多出口 IP 与故障隔离,代价是集中去重与状态管理的新复杂度。

为什么重要

单机有三个天花板:CPU/带宽、出口 IP、单点故障。分布式不是炫技而是这三件事的解;但过早分布式是采集工程最常见的过度设计——先确认 asyncio 单机(kp-019)真的不够。

前置知识

kp-021(任务状态与去重)、kp-019(异步 worker)。

核心概念

  • 中央队列:Redis(list/zset,轻量首选)或 RabbitMQ/Kafka(强消息语义)。
  • 共享去重:去重集合集中放 Redis(SET/BF),所有 worker 共用。
  • worker 无状态化:worker 只做「取任务 → 抓取 → 回写」,自身不存状态,随时可扩可缩。
  • scrapy-redis:把 Scrapy 的 Scheduler 与去重器替换为 Redis 实现的现成方案。

原理与机制

最小分布式拓扑:

                 ┌──────────────┐
  种子/新增任务 → │ Redis 队列    │ ← 共享去重集合(SET/BF)
                 │ LPUSH/BRPOP  │
                 └──┬────┬─────┘
              BRPOP │    │ BRPOP
             ┌──────┴┐  ┌┴───────┐
             │worker1│  │worker2 │  (workerN 同构, 各绑代理出口, kp-020)
             └──────┬┘  └┬───────┘
                    ↓    ↓
              共享存储(SQLite/PG/对象存储) → 结果侧统一清洗

worker 骨架(Redis 阻塞取任务):

import redis, requests

r = redis.Redis()
while True:
    _, url = r.blpop("crawler:queue")            # 阻塞式取, 无任务时挂起
    if r.sismember("crawler:seen", url):         # 共享去重
        continue
    try:
        resp = requests.get(url, timeout=(5, 20))
        save(resp)                                # 结果回写共享存储
        r.sadd("crawler:seen", url)
    except requests.RequestException:
        r.lpush("crawler:retry", url)             # 失败进重试队列, 不丢任务

容量估算经验:单 worker 峰值受限于限速(kp-017)与出口质量;扩容优先加 worker(IP 分散),队列与去重的 Redis 单实例足以支撑千万级任务。

实例或案例

全站 50 万页面存档任务:单机 asyncio 跑 26 小时且中途一次崩溃全部重来;改为「Redis 队列 + 4 个容器化 worker」后 7 小时完成,且中途 2 次 worker 崩溃只损失各自在途批。分布式带来的不只是快,更是故障半径的缩小。

常见误区

  • 误区一:需求千页级就上分布式。 千页任务 asyncio + 良好限速单机分钟级完成;分布式的运维成本(Redis、部署、监控)远超收益。
  • 误区二:去重留在各 worker 内存里。 多 worker 必然重复抓取且互相不知道;去重集合必须集中。
  • 误区三:忽略登录态与环境一致性。 多 worker 同时用同一账号高频访问是自曝;账号轮换与 kp-035 环境问题在分布式下被放大。

自测题

  1. 分布式解决单机三个什么问题?

答:吞吐天花板(水平加机器)、出口 IP 单一(worker 各绑代理)、单点故障(worker 挂了任务仍在队列)。

  1. 为什么去重必须集中化?

答:各 worker 独立去重会互不知情地重复抓同一 URL,浪费请求并加剧风控暴露。

  1. 判断「是否需要分布式」的第一问?

答:单机 asyncio + 限速已把资源用满且仍不达标/不可靠吗?不是则不要上分布式。

与其他知识点的关系

kp-018 + Redis 队列 ≈ scrapy-redis;kp-020 提供 worker 出口;kp-036 是本篇的容器化/云端形态。

延伸阅读

scrapy-redis 项目 README;Redis 官方文档 list 命令族(BLPOP 语义)。

相关知识点

学习进度