
前阵子我们有个 Python 采集任务,白天跑得好好的,晚上成功率突然往下掉。
第一反应当然是代理不行。
换了一批,没用。又把重试次数从 3 次提到 5 次,情况反而更糟:请求量上去了,有效数据没多多少,日志倒是刷得飞快。
最后把请求记录按状态拆开才发现,问题根本不全在代理。
有一部分是连接超时,有一部分是目标页面本来就不存在,还有一部分是登录状态失效。原来的代码不管遇到什么异常,统一换代理重试。结果就是,正常代理被当成坏代理踢掉,失效账号带着一批新出口继续撞,越救越乱。
这也是我现在设计代理层时最先避开的坑:
代理层不是一个返回 IP 的函数,更不是“请求失败就换一条”的重试插件。
它真正要解决的,是哪个任务该用哪类出口、要用多久,以及失败以后该怪谁。
我一般不会先问代理池怎么建,而是先看请求有没有上下文。
采集公开列表页,前后请求互不相关,可以一批请求轮换出口。只要并发别太激进,调度相对简单。
但登录账号、连续翻页、地区切换这类任务不一样。第一次请求从上海出去,第二次突然切到北京,Cookie 没变,出口却跳了。站在目标网站那边看,这个会话多少有点可疑。
所以这类任务需要的不是随机代理,而是会话绑定:
session_id → proxy_id一个账号或一段连续任务,在租约结束前固定使用同一出口。代理失效了再换,而不是每发一次请求就抽一次签。
我见过不少项目把“代理轮换频率高”当成能力,实际上轮换不是越快越好。公开页面看吞吐,登录会话看稳定,这两种任务本来就不该共用同一套调度方式。
代理层最重要的功能,不是选择,而是反馈。
一次请求失败,原因可能完全不同:
前四类可能和代理有关,后几类通常不是。
如果代码里只有一个宽泛的 except Exception,代理层就会像一个脾气很急的运维:哪里报警都先重启,至于为什么报警,之后再说。
我更愿意让请求层先给失败归类,再决定动作:
if failure_type == "proxy_connect":
disable_proxy(proxy_id)
retry_with_new_proxy()
elif failure_type == "rate_limited":
cool_down(proxy_id)
retry_later()
elif failure_type == "account_restricted":
stop_account_task()
else:
raise RequestError()这里有个很实用的判断:
重试,不一定要换代理;换代理,也不代表应该立刻重试。
比如目标网站返回服务端错误,换十条代理大概率还是一样。反过来,如果代理认证已经失败,就没必要让它继续留在池里接受下一次调度。
我们之前吃过一次亏,把所有 403 都算在代理头上。跑了半小时,可用池越来越小。后来才发现其中一批页面必须登录才能访问,代理只是替账号背了锅。
这种错很隐蔽。监控上看是“代理成功率下降”,实际上是业务判断写歪了。
项目规模小时,所有代理放在一个列表里,随机取一条,确实能跑。
问题是任务一多,互相影响就出来了。
高频列表采集容易触发限制,登录任务又要求出口稳定。如果两者共用一个池,前者不断消耗和降权,后者就跟着换出口。最后你会发现,每个任务单独看都没那么糟,放在一起却总出问题。
我通常会按业务拆成几个逻辑池,例如:
这些池不一定对应不同的代理来源,重点是调度策略和状态分开。
公开页面池可以优先利用吞吐;登录会话池要限制占用并保持租约;指定地区池还要校验出口位置。别让一个跑列表页的高并发任务,把登录任务正在使用的稳定出口顺手打废。
到了这一步,代理层的职责其实已经比较清楚了:
任务提交请求
↓
代理路由器判断业务类型
↓
从对应池中分配或续用代理
↓
请求完成后回传结果
↓
代理降权、冷却、恢复或停用不需要一上来就写很复杂的评分算法。记录成功次数、连续失败次数、过期时间和冷却时间,已经够解决大多数早期问题。
如果现在让我从零给一个 Python 采集项目加代理层,我不会先建十几个类,也不会先研究一套精确到小数点后两位的评分模型。
第一版我只做四件事:
第一,业务代码不直接获取代理,只提交任务类型和会话标识。
第二,公开请求允许轮换,登录请求绑定固定出口。
第三,至少分清代理连接失败、目标限流、账号异常和业务错误。
第四,日志里保留任务、代理标识、目标域名、耗时和失败类型。
这四件事做好,后面再增加并发限制、共享租约、自动补充和多来源切换,都有地方落。
最怕的是一开始图省事,把获取代理、发送请求、异常重试和页面解析全写在同一个函数里。任务跑到一定规模后,谁也说不清一次失败到底在哪发生,只能不停加 if。
代码看着没几百行,改起来比几千行还费劲。
Python 采集任务里的代理层,不应该围着“怎么多换几个 IP”设计。
它更像一个网络资源调度器:
该固定的时候固定,该轮换的时候轮换;知道一条代理现在能不能用,也知道请求失败以后该不该怪它。
代理供应端当然会影响稳定性,但很多所谓的“代理质量问题”,其实是任务没有分池、会话没有绑定、失败没有归因。
这三件事没处理好,换多少代理都只是把问题往后推。
我现在判断一个代理层能不能进生产,标准也很简单:出问题时,能不能说清楚是哪个任务、哪类出口、因为什么失败,以及下一步该换代理、停账号,还是直接修请求代码。
能说清楚,才算真的有代理层。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。