
哈喽大家好!今天跟大家唠一手我线上长期稳定运行的实战方案,聊聊Redis + Scrapy 分布式爬虫的代理IP落地玩法,全程第一视角、干货拉满,直接能拿去上线用。
做爬虫久了我发现一个很有意思的现象:大多数分布式爬虫崩掉、爬不动、频繁403封禁,根本不是爬取逻辑写得烂,而是代理IP没管好。
很多朋友单机爬虫跑得好好的,一上分布式多节点就翻车:IP重复乱用、无效IP一直占坑、明明有代理却一直失败、IP秒封……其实核心问题很简单:单机代理逻辑,完全撑不起分布式场景。
所以我专门打磨了这套 Redis 统一代理调度方案,把代理池集中管理、自动换IP、自动剔除废IP、多节点互不冲突。今天从零带大家完整复盘,从踩坑到架构、代码到生产优化,一次性讲透。
我刚做分布式爬虫的时候,也图省事,直接照搬了单机的代理写法:本地存一份IP列表、随机抽取使用。结果部署多台机器后,各种问题连环爆,相信很多小伙伴都踩过同款坑:
1. 代理资源严重浪费:每台爬虫节点各玩各的,有的节点IP用光了疯狂报错,有的节点一堆IP闲置,资源极其不均衡。
2. 失效IP一直恶心人:这台机器判定某个IP废了,其他机器不知道,还在反复用这个烂IP发请求,白白浪费带宽和爬取时间。
3. 多节点撞车疯狂封IP:没有全局管控,多台机器随机乱拿IP,同一个IP短时间被多次复用,访问频次直接超标,封号速度快到离谱。
4. 纯人工维护,巨累:失效IP不会自动清理、不会自动复活,全靠手动删、手动加,根本扛不住7×24小时持续爬取的项目。
所以我直接敲定改造思路:把代理IP从本地独立管理,改成 Redis 全局中心化调度。所有爬虫节点共用一个代理池,状态统一同步、烂IP自动踢、可用IP智能轮换,彻底根治分布式代理乱象。
不讲空洞理论,直接说我实际落地的架构。整套方案抛弃本地静态IP列表,全程靠Redis做核心调度,完美适配Scrapy分布式异步爬取,四层结构,清晰好懂、扩展性拉满。
1. 代理数据源层:对接第三方动态代理服务,定时拉取新鲜IP,持续给爬虫提供可用代理资源,告别静态写死IP的尴尬。
2. Redis核心调度层:用Redis集合、有序集合、哈希结构,分别存放可用IP、失效IP、IP权重和调用次数。全局统一管控,所有爬虫节点数据实时同步,杜绝信息不同步的问题。
3. Scrapy自定义中间件层:废掉原生代理中间件,自己手写中间件逻辑。实现请求自动拿IP、异常自动标记废IP、失败自动换IP重试,形成完整闭环。
4. 分布式爬虫节点层:多台服务器部署爬虫,所有节点共享Redis任务队列和代理池,不用单独配置,想扩容直接加机器就行,零成本横向扩展。
对比传统单机代理玩法,提升真的肉眼可见:彻底解决多节点IP冲突、无效IP自动清理、请求成功率大幅提升、支持无限扩容节点,全程自动化运维,几乎不用人工干预。
下面所有代码都是我线上稳定运行的版本,没有残缺片段、没有无效代码,大家直接复制替换即可使用,开箱即用。
只需要安装三个核心依赖,适配分布式调度和Redis连接:
pip install scrapy scrapy-redis redis我单独封装了工具类,把IP新增、随机获取、失效剔除、状态重置全部封装好,解耦爬虫业务,后续改逻辑、加功能都超级方便。
import redis
import random
# Redis连接配置(生产环境务必开启密码、私有化部署)
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
REDIS_DB = 0
# Redis键名统一规范,方便后续运维排查问题
PROXY_VALID_KEY = "spider:proxy:valid" # 可用代理集合
PROXY_INVALID_KEY = "spider:proxy:invalid" # 失效代理集合
class RedisProxyClient:
def __init__(self):
self.redis_cli = redis.Redis(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
decode_responses=True,
socket_timeout=5
)
# 添加可用代理IP到Redis池
def add_valid_proxy(self, proxy):
self.redis_cli.sadd(PROXY_VALID_KEY, proxy)
# 随机获取一个可用代理
def get_random_proxy(self):
valid_proxies = self.redis_cli.smembers(PROXY_VALID_KEY)
if not valid_proxies:
return None
return random.choice(list(valid_proxies))
# 标记代理失效,转移到失效集合
def mark_proxy_invalid(self, proxy):
if not proxy:
return
self.redis_cli.srem(PROXY_VALID_KEY, proxy)
self.redis_cli.sadd(PROXY_INVALID_KEY, proxy)
# 重置失效IP(用于定时复活检测)
def reset_invalid_proxy(self, proxy):
self.redis_cli.srem(PROXY_INVALID_KEY, proxy)
self.redis_cli.sadd(PROXY_VALID_KEY, proxy)
# 获取当前可用代理总数
def get_valid_count(self):
return self.redis_cli.scard(PROXY_VALID_KEY)原生代理中间件完全满足不了分布式需求,我直接自定义中间件,实现自动分配IP、异常剔除、失败重试换IP,是整套方案的灵魂所在。
from .redis_proxy import RedisProxyClient
class RedisProxyMiddleware:
def __init__(self):
self.proxy_client = RedisProxyClient()
# 连续失败3次判定IP失效,可根据业务自行调整
self.fail_limit = 3
# 本地记录各IP失败次数,辅助判断失效
self.proxy_fail_count = {}
# 每次请求前自动分配代理
def process_request(self, request, spider):
# 跳过本地请求、手动指定代理的特殊场景
if request.meta.get("proxy") or "127.0.0.1" in request.url:
return
proxy = self.proxy_client.get_random_proxy()
if proxy:
request.meta["proxy"] = proxy
spider.logger.info(f"本次请求分配代理:{proxy}")
# 请求异常处理,自动剔除劣质IP
def process_exception(self, request, exception, spider):
proxy = request.meta.get("proxy")
if not proxy:
return
# 累计失败次数
self.proxy_fail_count[proxy] = self.proxy_fail_count.get(proxy, 0) + 1
# 达到阈值,剔除失效IP
if self.proxy_fail_count[proxy] >= self.fail_limit:
self.proxy_client.mark_proxy_invalid(proxy)
spider.logger.warning(f"代理{proxy}多次请求失败,已移出可用池")
del self.proxy_fail_count[proxy]
# 重试请求,自动更换新IP
return request.replace(dont_filter=True)这里是分布式调度的关键配置,一定要关掉原生代理中间件,启用自定义中间件,否则会冲突报错。
# 开启scrapy-redis分布式调度
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 重启爬虫不丢失任务,非常实用
SCHEDULER_PERSIST = True
# 分布式全局去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# Redis连接信息
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
# 中间件优先级配置,禁用原生代理中间件
DOWNLOADER_MIDDLEWARES = {
'your_spider.middlewares.RedisProxyMiddleware': 110,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None,
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': 400,
}
# 爬虫并发、延时配置,适配代理池承载力
CONCURRENT_REQUESTS = 32
DOWNLOAD_DELAY = 0.3
RANDOMIZE_DOWNLOAD_DELAY = True
# 失败重试配置,覆盖常见异常状态码
RETRY_TIMES = 2
RETRY_HTTP_CODES = [403, 429, 500, 502, 503, 504]项目启动前,批量把代理IP写入Redis,生产环境可以对接第三方API自动刷新,不用手动改列表。
from redis_proxy import RedisProxyClient
if __name__ == "__main__":
proxy_client = RedisProxyClient()
# 替换成自己的代理IP,支持批量写入
proxy_list = [
"http://xxx.xxx.xxx.xxx:xxxx",
"http://xxx.xxx.xxx.xxx:xxxx",
"http://xxx.xxx.xxx.xxx:xxxx"
]
# 批量存入Redis可用代理池
for proxy in proxy_list:
proxy_client.add_valid_proxy(proxy)
print(f"成功初始化{len(proxy_list)}个代理IP至Redis代理池")基础版本跑通之后,我又迭代了几个生产级优化,专门解决长期运行、高并发场景下的各种小毛病,让爬虫稳如老狗。
大部分被剔除的IP不是永久报废,只是临时被目标站点封禁。我用定时任务定期检测失效池里的IP,能正常使用就自动挪回可用池,最大化利用代理资源,省下不少代理费用。
代理IP质量参差不齐,有的稳如磐石,有的一碰就废。我通过Redis有序集合统计每个IP的请求成功率、响应速度,优先调用高质量IP,劣质IP低频试用,整体请求成功率大幅提升。
一个IP在A网站能用,不代表在B网站能用。我按域名拆分独立代理池,不同项目、不同站点互不干扰,避免一个站点封IP连累全局代理报废。
实时监控Redis可用IP数量,一旦低于设定阈值,自动调用第三方代理API刷新新增IP,保证代理池永远有充足资源,不会出现爬虫无IP可用、停摆卡死的情况。
这套方案我踩了无数坑,整理几个最容易翻车的点,帮大家少走弯路、一次跑通项目:
1. 多节点不要重复初始化代理:分布式环境下,代理刷新和初始化脚本只跑一台机器就行,多节点重复写入会造成IP冗余、资源浪费、调度混乱。
2. 务必禁用原生代理中间件:新旧代理中间件会直接冲突,导致代理不生效、请求报错、随机失败,一定要在配置里彻底置空原生中间件。
3. 精准判断IP失效,别乱删IP:不是所有请求异常都是IP的问题,网络波动、服务器超时都很常见。做好异常分类,避免误删优质可用IP。
4. Redis一定要配置容错机制:生产环境Redis断开、超时很常见,务必配置超时和自动重连,不然爬虫会集体报错瘫痪。
5. 并发和代理数量要匹配:代理少、并发高,必然导致IP极速复用、快速封禁。根据代理池规模合理调整爬虫并发,适配才是稳跑的关键。
这套轻量化、高可用的 Redis+Scrapy 分布式代理方案上线后,我的爬虫项目稳定性直接拉满:多节点IP调度完全统一,再也没有出现过IP冲突复用的问题;无效IP自动清洗替换,请求成功率从60%稳步涨到95%以上;支持轻松扩容十几台爬虫节点,爬取效率直接翻3倍;全程自动化运维,几乎不用人工看管。
相比网上碎片化的残缺教程,这套方案是完整闭环、可直接投产的实战架构,不管是小规模数据采集,还是大规模分布式集群爬取,都能完美适配,是分布式爬虫代理调度的最优落地方案之一。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。