6.3 分布式爬取:何时多机、怎么协同 本节摘要:分布式不是升级包,是架构换挡:三要素是共享请求队列、共享去重指纹、任务拆分策略。本节先给出"先单机后多机"的判断依据,再用队列与指纹共享搭一个最小分布式,最后讲多机带来的新问题——优雅停机、任务均衡与数据汇流。 "抓得慢"是催生分布式最常见的动机,却往往是最不成立的那个。本节从判断开始:先确认你的慢,真是单机瓶颈。 先判断:你真的需要多机吗 单机采集的吞吐上限通常远超新手预期。跑不快时按序排查: 四种情况都不成立的真正单机瓶颈,多是"目标站限你的速"——加机器毫无意义,对方按 IP 限流,十台机器等于十倍封禁概率。只有待抓队列大到单进程消化不过来、且目标站不按 IP 严限时,分布式才成立。
本节摘要:分布式不是升级包,是架构换挡:三要素是共享请求队列、共享去重指纹、任务拆分策略。本节先给出"先单机后多机"的判断依据,再用队列与指纹共享搭一个最小分布式,最后讲多机带来的新问题——优雅停机、任务均衡与数据汇流。
"抓得慢"是催生分布式最常见的动机,却往往是最不成立的那个。本节从判断开始:先确认你的慢,真是单机瓶颈。
单机采集的吞吐上限通常远超新手预期。跑不快时按序排查:
1. 单域并发给足了吗 —— CONCURRENT_REQUESTS_PER_DOMAIN 长期贴顶? 2. 延迟是不是过保守 —— DOWNLOAD_DELAY 1 秒以上且节流天花板未调? 3. 是不是 CPU 瓶颈 —— 解析比下载慢(响应体大、正则重)? 4. 网络出口限速了吗 —— 带宽占用率查过吗?
四种情况都不成立的真正单机瓶颈,多是"目标站限你的速"——加机器毫无意义,对方按 IP 限流,十台机器等于十倍封禁概率。只有待抓队列大到单进程消化不过来、且目标站不按 IP 严限时,分布式才成立。还有一个正当理由与速度无关:多出口 IP 提升采集的容灾与区域覆盖。
分布式爬取的三要素,每一件都对应框架的一个挂载点:
要素一,共享请求队列。 各机器不再各自排队,而是从一个公共队列取票发。用框架的调度器挂载点接入外部队列:
# settings 配置模块:接入共享调度 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 社区扩展提供 REDIS_HOST = "10.0.8.20" REDIS_PORT = 6379
要素二,共享去重指纹。 3.1 节说过指纹默认在进程内存——多机各存各的等于没去重。挂上共享指纹过滤器:
# settings 配置模块:共享指纹 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" SCHEDULER_PERSIST = True # 指纹与队列跨任务持久:断点续抓的分布式版
要素三,任务拆分。 按站点、按分类、按地址段把待抓集合拆给各机器,避免全部机器扑向同一站点——那既浪费协同又触发对方风控。拆分粒度与 5.3 节的站点评估挂钩:大站按分类拆、小站整站分派。
两台机器共同消费一个队列时的日志形态:
# 机器A DEBUG: Crawled (200) books.example.com/category/3 # 机器B(同一时刻) DEBUG: Crawled (200) books.example.com/category/7 # 两边统计合计,且无重复地址 —— 指纹共享生效
背景:单机日更八十万页的采集任务,响应延迟稳定但队列消化速度不足——排查确认并发、延迟、CPU、带宽四项都没顶到,纯粹是待抓集太大,且目标站按站点级而非 IP 级限流。满足多机的两个前提。
操作分四步。第一步起中央存储:一台机器装 Redis,作为共享队列与指纹集合的家。第二步改配置:两处挂载(共享调度、共享指纹),加持久开关,各采集机配置一致。第三步预拆任务:把八十万页按站点分类切成三份,起始清单分别发给三台机器的 Spider——注意是"预拆"而不是"抢队列",抢队列模式空闲机器会自动拿活,但预拆让监控与失败重跑的边界清晰得多。第四步对账:三台机器的产出写同一个 MySQL(6.2 的唯一键防重此处分外要紧),收工后与待抓总量核账。
# 升级后的配置差异(三台机器一致) SCHEDULER = "scrapy_redis.scheduler.Scheduler" DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" SCHEDULER_PERSIST = True REDIS_HOST = "10.0.8.20"
结果:日更任务从单机的七小时缩到三机的两小时四十分,接近线性——因为任务按分类天然可拆,且对端不限单机 IP。指纹集合跨机生效,三机全程零重复下载。解读:这次升级的瓶颈判断、任务拆分、落库防重三件事,前两件决定"该不该多机、怎么拆",第三件决定"数据敢不敢用"。
变式:任务不可整拆时(单站点大纵深),改用"分层消费"——机器 A 专扫列表层产出详情清单,机器 B 与 C 只消费清单抓详情,队列分工代替地址分工;再不够时按地址哈希分片。两种形态的协同纪律相同:任务边界清晰、停机走信号、失败件登记后由原属机器补抓。
单机不存在的问题在多机全冒出来。优雅停机:直接杀进程会丢飞行中的票,正确姿势是发关闭信号让引擎收尾(第 4 章 errback 的失败登记在停机场景尤其重要)。任务均衡:队列消费有快有慢,慢机拖尾时把大站点拆小、或给不同机器配不同的并发参数。数据汇流:各机器的落库管道写同一个库,唯一键防重(6.2 节)从"建议"升级为"必须"——多机重复产出同一货物在分布式下是常态而非异常。

💡 关键直觉:分布式的本质是把"队列与指纹"从进程内存搬进公共存储,搬完之后每台机器跑的还是你已经会写的单机爬虫。难的不是爬虫,是协同纪律。
多机协同稳定运行后,日常巡检比单机多了几项。队列水位:共享队列的待发数长期不降说明有机器掉线——消费方数量要跟采集机清单核对。指纹容量:指纹集合只增不减,长年累月要靠任务维度清理过期指纹,避免中央存储无限膨胀。时钟一致:三台机器系统时间偏差会影响日志对账与告警判读,定时校时是基础运维。版本一致:采集机的工程版本必须统一,版本漂移会让同一任务产出两套字段口径——发布流程里加一条"全部机器同版本才开跑"的检查即可。
# 巡检脚本的核心判据(示意) 队列水位 < 阈值 否则:查消费方是否齐全 采集机在线数 == 期望数 否则:告警并暂停派新任务 版本号 全员一致 否则:阻断发布
单机多机都会跑了,下一节把工程送出去:部署成无人值守的定时任务。