Python 分布式爬虫架构实战:并发、队列、去重与断点续爬
前言随着爬取规模扩大单机单线程爬虫效率极低面对海量目标链接、高频采集需求时完全无法适配。普通单线程、多线程爬虫还存在任务重复、数据丢失、IP 受限、崩溃重启需重爬等一系列问题。分布式爬虫核心就是将爬取任务拆分通过多节点、多并发协同采集搭配统一任务队列、去重过滤器、持久化存储实现高效率、高可用、大规模数据采集。本文从零落地分布式爬虫完整架构涵盖线程 / 协程并发、Redis 任务队列、布隆过滤器去重、断点续爬、分布式 IP 池、异常容错全部提供可直接运行的工业级代码无缝衔接前面反爬、JS 逆向、指纹伪装内容形成完整爬虫技术闭环。一、爬虫并发方案选型线程 / 协程 / 多进程1.1 三大并发方式核心区别多线程适合 IO 密集型爬虫请求等待期间切换线程开发简单、开销小受 GIL 锁限制CPU 性能弱。协程异步轻量级并发极高并发量上万连接无压力异步请求库适配反爬指纹库是爬虫首选。多进程绕过 GIL适合 CPU 密集型解析场景进程开销大爬虫场景极少单独使用。1.2 爬虫最优组合协程 线程池 Redis 分布式队列兼顾并发量、稳定性、资源占用适配绝大多数中小型分布式采集项目。二、Redis 环境部署与基础使用分布式核心依赖 Redis用于任务队列、去重存储、计数器、断点标记。2.1 核心安装bash运行pip install redis aiohttp2.2 Redis 基础连接封装python运行import redis class RedisClient: def __init__(self): self.client redis.Redis( host127.0.0.1, port6379, db0, decode_responsesTrue ) # 左侧入队 def lpush(self, key, value): return self.client.lpush(key, value) # 右侧出队 def rpop(self, key): return self.client.rpop(key) # 集合去重 def sadd(self, key, value): return self.client.sadd(key, value) def sismember(self, key, value): return self.client.sismember(key, value)三、分布式任务队列设计3.1 队列结构设计task_queue待爬取 URL 队列List 类型done_set已爬取 URL 集合Set 类型fail_queue失败重试队列3.2 生产消费者模型生产者批量生成链接推入 Redis 队列消费者多节点拉取队列任务执行爬取、解析、入库3.3 基础生产代码python运行redis_cli RedisClient() def produce_task(url_list): 生产任务写入待爬队列 for url in url_list: # 去重判断 if not redis_cli.sismember(spider_done, url): redis_cli.lpush(spider_task, url) print(f任务写入完成总量{len(url_list)})四、协程异步分布式爬虫核心实现基于 aiohttp 实现异步请求结合 Redis 队列单机器实现分布式效果多机器共享 Redis 即可集群部署。python运行import aiohttp import asyncio from redis_client import RedisClient # 初始化redis redis_cli RedisClient() HEADERS { User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/126.0.0.0 Safari/537.36 } async def fetch(session, url): 异步请求函数 try: async with session.get(url, headersHEADERS, timeout10) as resp: text await resp.text() print(f爬取成功{url} 状态码{resp.status}) # 标记已完成 redis_cli.sadd(spider_done, url) return text except Exception as e: print(f爬取失败{url} 错误{str(e)}) # 写入失败队列等待重试 redis_cli.lpush(spider_fail, url) return None async def consumer(): 消费者循环读取队列 connector aiohttp.TCPConnector(limit20) async with aiohttp.ClientSession(connectorconnector) as session: while True: url redis_cli.rpop(spider_task) if not url: await asyncio.sleep(1) continue await fetch(session, url) def run_spider(worker_num10): 启动多协程消费者 loop asyncio.get_event_loop() tasks [consumer() for _ in range(worker_num)] loop.run_until_complete(asyncio.gather(*tasks)) if __name__ __main__: # 模拟写入测试任务 test_urls [fhttps://www.example.com?page{i} for i in range(1,50)] produce_task(test_urls) # 启动爬虫 run_spider(worker_num15)五、高效去重集合去重 布隆过滤器5.1 Redis 集合去重小体量优点简单精准、支持删除缺点海量 URL 内存占用高适合十万级以内。前文代码已集成sadd sismember直接复用即可。5.2 布隆过滤器亿级海量去重亿级链接场景Redis 集合占用过高使用布隆过滤器极小内存实现超高去重。安装依赖bash运行pip install pybloom-live简易布隆过滤器实现python运行from pybloom import ScalableBloomFilter # 可扩展布隆过滤器 bloom ScalableBloomFilter(initial_capacity1000000, error_rate0.001) def url_is_exist(url): if url in bloom: return True bloom.add(url) return False结合使用逻辑优先布隆过滤器粗去重异常链接二次校验兼顾性能与准确率。六、断点续爬与失败重试机制6.1 断点续爬原理所有任务持久化在 Redis爬虫程序异常崩溃、手动停止后重启直接消费剩余队列无需从头爬取。6.2 失败任务自动重试单独维护失败队列定时二次消费限制重试次数防止死循环python运行def retry_fail_task(max_retry3): 重试失败队列任务 for _ in range(max_retry): url redis_cli.rpop(spider_fail) if not url: break redis_cli.lpush(spider_task, url)七、分布式代理 IP 池集成多节点共享代理池统一 IP 调度避免单 IP 高频封禁代理 IP 存入 Redis List消费者爬取前随机取出代理无效 IP 自动剔除、补充新 IP多节点共用 IP 池统一管控。简易代理获取示例python运行import random def get_proxy(): proxy_list redis_cli.client.lrange(proxy_pool, 0, -1) return random.choice(proxy_list) if proxy_list else None八、分布式爬虫异常容错优化并发限制通过 TCPConnector 限制最大连接数防止 IP 封禁超时控制全局请求超时卡死自动释放任务随机延时协程内添加随机 sleep降低风控识别日志持久化保存爬取日志便于问题排查数据批量入库减少数据库 IO提升整体效率。九、分布式集群部署方案统一 Redis 服务所有服务器连接同一个 Redis多机部署消费者多台机器同时消费任务队列横向扩容单一生产者一台机器负责生成任务避免重复生产统一存储MySQL/MongoDB 统一入库集中管理数据。十、全篇爬虫体系总结基础爬虫requests 单线程进阶反爬UA / 代理 / Cookie / 行为模拟中级突破验证码破解、Selenium 自动化高阶对抗JS 逆向、TLS 指纹、浏览器伪装大规模采集协程并发、Redis 分布式、布隆去重、集群部署整套技术栈覆盖零基础到工业级爬虫全部场景可直接应用于数据分析、内容采集、行业调研等合规场景。