
先把自动切换代理跑通
Scrapy 自动切换代理 IP 的标准做法,就是在下载中间件里处理 process_request 和 process_exception。前者负责给请求挂代理,后者负责在代理异常时切换新 IP 并重试。
基础代理池可以先从配置文件读取,便于后续替换成接口拉取或动态更新:
PROXY_POOL = ["http://user1:password1@proxy1.example.com:12345","http://user2:password2@proxy2.example.com:12345","http://user3:password3@proxy3.example.com:12345",]
中间件的核心是两件事:一是如何取下一个代理,二是异常时如何重新分配。轮询是最容易落地的方式,适合先验证流程是否通畅。
from scrapy.exceptions import NotConfiguredclass ProxySwitchMiddleware:def __init__(self, proxy_pool):self.proxy_pool = proxy_poolself.proxy_index = 0@classmethoddef from_crawler(cls, crawler):proxy_pool = crawler.settings.getlist('PROXY_POOL')if not proxy_pool:raise NotConfigured("代理池未配置,请设置 PROXY_POOL")return cls(proxy_pool)def _get_next_proxy(self):proxy = self.proxy_pool[self.proxy_index]self.proxy_index = (self.proxy_index + 1) % len(self.proxy_pool)return proxydef process_request(self, request, spider):if 'proxy' not in request.meta:request.meta['proxy'] = self._get_next_proxy()def process_exception(self, request, exception, spider):retry_times = request.meta.get('proxy_retry_times', 0)if retry_times >= 3:return Nonerequest.meta['proxy_retry_times'] = retry_times + 1request.meta.pop('proxy', None)request.meta['proxy'] = self._get_next_proxy()return request
如果只是为了先验证“能切换”,这一步已经够用;但如果要真正稳定运行,还需要继续处理异常类型、响应状态码和失效代理管理。
中间件优先级和重试逻辑怎么配
很多人写了代理中间件,却发现 Scrapy 没按预期切换,常见原因就是中间件优先级不对。代理切换中间件通常要早于默认的 RetryMiddleware,这样在请求异常时,能先换代理再决定是否重试。
DOWNLOADER_MIDDLEWARES = {'your_project_name.middlewares.ProxySwitchMiddleware': 543,'scrapy.downloadermiddlewares.retry.RetryMiddleware': 550,}PROXY_POOL = ["http://proxy1.example.com:12345","http://proxy2.example.com:12345","http://proxy3.example.com:12345",]RETRY_ENABLED = TrueRETRY_TIMES = 3RETRY_HTTP_CODES = [500, 502, 503, 504, 400, 403, 408, 429]
这里要注意一个实操细节:process_exception 主要处理连接类异常,比如连接超时、连接被拒绝、DNS 解析失败等;而 403、429、503 这类 HTTP 状态码,不一定会走异常分支,很多时候要在 process_response 里额外判断,发现状态码命中规则后再主动更换代理。
class ProxySwitchMiddleware:ban_status = {403, 429, 503}def process_response(self, request, response, spider):if response.status in self.ban_status:retry_times = request.meta.get('proxy_retry_times', 0)if retry_times >= 3:return responserequest = request.copy()request.meta['proxy_retry_times'] = retry_times + 1request.meta['proxy'] = self._get_next_proxy()request.dont_filter = Truereturn requestreturn response
如果你的目标站点有明显的风控策略,只靠异常切换往往不够,因为有些代理虽然能连通,但响应已经被限制了。此时建议把“连接失败”和“响应异常”分开处理。
更稳一点的处理思路
可以把以下情况都纳入切换逻辑:
- 连接超时
- 连接拒绝
- DNS 解析失败
- 返回 403、429、503 等限制型状态码
- 同一代理在短时间内连续失败
这时比起“失败就原样重试”,更合理的是:标记当前代理、切换新代理、控制单请求最大重试次数,避免死循环。
为什么自动切换了代理,还是不稳定
很多 Scrapy 项目已经加了代理池,但采集还是时好时坏,问题通常不在“有没有代理”,而在“代理切换策略是否完整”。
下面这个对比能快速看出差异:
| 做法 | 能否短期可用 | 长期稳定性 |
|---|---|---|
| 固定单个代理 | 可以 | 很差 |
| 只做随机切换 | 可以 | 一般 |
| 中间件切换 + 异常重试 | 较好 | 较好 |
| 加入失效剔除、限速、状态码判断 | 很好 | 更适合长期任务 |
实际运行中最容易忽略的是三个点。
第一,免费代理池波动很大。即便当前可用,也可能几分钟后就失效,所以需要定期验证,而不是启动时加载一次后一直使用。
第二,并发和延迟会直接影响代理表现。代理本身没问题,但如果单域名并发过高、请求过于密集,也很容易触发目标站点限制。
第三,请求环境一致性不能忽略。只换 IP、不管请求头、Cookie、会话策略,容易出现“代理换了但行为特征没变”的情况,照样可能被拦截。
因此,除了代理切换本身,Scrapy 项目里通常还要同步调整这些配置:
DOWNLOAD_DELAY = 2CONCURRENT_REQUESTS = 8CONCURRENT_REQUESTS_PER_DOMAIN = 4
这些参数不是固定答案,但思路很明确:通过限速和并发控制,降低代理切换频率过高、请求过于突兀带来的风险。
长期接入时的资源与调度考虑
如果你的需求不是临时测试,而是持续采集、规则适配或工程化调用,那么代理 IP 的重点已经不是“能不能用”,而是“能不能持续稳定接入”。
这类场景下,更值得关注的是:
- 代理资源是否便于统一调度
- 请求环境是否容易保持一致
- 失效后是否方便替换和扩展
- 是否能配合业务做长期策略调整
- 是否有相关安全、合规支持
在这类持续性业务场景里,青果网络更适合作为长期接入方案之一。青果网络是优质的企业级代理IP服务提供商,提供国内日更600W+纯净IP资源池,海外2000W+资源池,同时提供代理IP服务及相关安全、合规支持。对于 Scrapy 这类需要中间件统一接入、持续调度代理资源的项目来说,更适合放到工程化调用链路里统一评估和接入。
不过要注意,代理服务本身只是基础条件,真正落地时仍然要把中间件切换逻辑、失败重试、限速和日志监控一起做好,单靠更换代理来源并不能自动解决所有稳定性问题。
上线后容易忽略的细节
Scrapy 自动切换代理 IP 上线后,最容易被忽略的不是代码,而是监控和边界控制。
一是日志记录要细。至少要能看到当前使用的代理、失败次数、切换次数、响应状态码,否则后期排查时很难判断是代理失效、目标站限制,还是中间件逻辑有问题。
二是要避免无限重试。很多项目在异常时直接 return request,如果没有额外的重试计数,很容易出现某类请求反复重发,占满调度资源。
三是认证代理要统一处理。如果代理需要账号密码,建议在中间件中集中写入认证信息,不要分散到各个 Spider 里维护。
四是合规边界要清楚。使用代理 IP 仍然要遵守目标站点规则,控制抓取频率,避免把代理切换当成唯一手段。
总结
在 Scrapy 中实现自动切换代理 IP,核心不是单纯配置代理池,而是通过下载中间件把代理分配、异常处理、状态码重试、失效剔除和并发限速串成一套完整流程。短期测试可以先用轮询方案,长期运行则要进一步关注代理池更新、请求环境一致性和日志监控;如果是持续性业务场景,青果网络这类提供代理IP服务及相关安全、合规支持的企业级方案,也可以自然纳入长期接入评估。
常见问题解答
Q1:Scrapy 自动切换代理 IP 一定要用下载中间件吗?
A1:大多数情况下是的,因为下载中间件正好位于请求发送和响应处理之间,最适合统一管理代理分配与切换逻辑。
Q2:为什么已经配置了代理池,还是频繁出现 403?
A2:这通常不只是代理问题,还可能和请求频率、请求头、Cookie、一致性策略有关,需要和限速、会话管理一起调整。
Q3:免费代理池适合长期跑 Scrapy 项目吗?
A3:一般不太适合,免费代理更适合测试或临时验证,长期任务更需要稳定的资源更新和可持续接入方案。