
带采集团队这些年,听得最多的一句话是"这家代理不行,换一家试试"。每次我都让对方先别动,把日志发我看看——排查下来,大部分所谓的"代理质量问题"换谁家都会原样复现:中间件优先级写错、IP 过了存活期还在用、重试沿用失效 IP、并发超过代理吞吐,清一色是配置和机制的错配,跟服务商没关系。
这篇把我平时排障的完整套路整理出来:五类高发原因,每条给诊断方法、修复步骤和验证标准,按信号对号入座就行,不用通读。
看异常信号直接跳到对应小节。下表按出现概率从高到低排,前两条通常能解决大部分问题:
异常信号 | 高发原因 | 对应小节 |
|---|---|---|
出口 IP 始终是本机 IP,代理像没配一样 | 中间件优先级写错,代理从未生效 | 原因一 |
日志出现 407 Proxy Authentication Required | 鉴权未通过 | 原因二 |
同一 IP 先成功后大量 ConnectionRefused、TCPTimedOut | IP 已过存活期 | 原因三 |
日志里 Retrying 多次,最终仍失败 | 重试沿用同一个失效 IP | 原因四 |
大面积 ReadTimeout,间歇出现 429 | 并发超过代理额定吞吐 | 原因五 |
多种信号同时出现的,按一到五的顺序排,别跳着查。
我接手任何一个"采集不稳定"的案子,第一件事都是确认这三样在不在,缺一样后面的步骤就执行不下去:
LOG_LEVEL = "DEBUG" 并指定 LOG_FILE。只有 DEBUG 级别才会记录每个请求实际使用的 proxy 字段,这是后面所有判断的依据。没开 DEBUG 就来问"为什么代理不生效"的,等于蒙着眼排障。-x 参数可以脱离 Scrapy 单独测代理连通性,把框架配置问题和代理链路问题切开。这是排障里最重要的一刀:分不清这两层,能在错误的方向上耗一整天。三样备齐后,建一个只请求 IP 回显接口的测试 spider,作为全程的验证基准:返回哪个出口 IP,一目了然。
这是我见过的头号问题,而且最冤——很多人代理配置本身没错,只是从来没被应用过。
诊断很简单:跑一遍测试 spider,返回的仍是本机 IP,就是它。
原理翻一眼 Scrapy 源码就明白:内置 HttpProxyMiddleware 的优先级是 750,并且它发现请求已设置 proxy 时会直接放行。所以自定义代理中间件必须排在 750 之前,写进 meta 的代理才会被应用。社区通行写法是 543:
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 543,
}中间件里通过 request.meta["proxy"] 写入接入地址即可。
验证标准:重跑测试 spider,返回 IP 变为代理出口,同时 DEBUG 日志的请求行里出现 proxy 字段。两个条件都满足才算修复,只满足一个继续查。
407 说明请求到了代理服务器,但鉴权没过。老规矩,先用 curl 切开两层:
curl -x http://host:port <IP回显接口地址>curl 同样返回 407,问题在鉴权配置本身;curl 正常而 Scrapy 报 407,问题在框架内的代理写法。
白名单方式:确认加进白名单的是服务器的出口公网 IP。这里有个坑我每年都要跟人讲好几遍:云服务器的出口 IP 和你 SSH 登录用的 IP 经常不是同一个,拿错 IP 加白名单是 407 反复出现的头号原因。用 curl 请求任意 IP 回显服务查一下出口 IP,别凭感觉填。
账密方式:凭据直接写进接入地址,格式 http://user:pass@host:port,写入 meta["proxy"] 即可,不需要额外加请求头。我见过不少人画蛇添足去拼 Proxy-Authorization 头,反而把自己绕进去。
验证标准:curl 与测试 spider 先后返回 200,且连续跑 10 分钟无 407,再进正式任务。
同一个 IP 前几次成功、随后集中爆 ConnectionRefused 或 TCPTimedOut,这个模式太典型了,基本可以直接锁定存活期问题。
诊断动作:去代理后台调 IP 提取记录,对比失败请求的时间戳和该 IP 的提取时间。时间差超过所选存活档位,就是拿失效 IP 在发请求,代码再怎么改都没用。
修复的关键认知是:存活档位要和单批任务耗时对齐,剩余寿命撑不完一批页面的 IP 就该提前弃用,而不是等它报错。 选服务的时候我会优先看档位灵不灵活,像极安代理的短效产品把存活期做成 1-15 分钟五档可选,到期自动失效、按每日 IP 数计费,单页耗时长的任务选长档,高频轮换选短档,档位选错换档就行,不用换产品线。程序侧配合记录每个 IP 的提取时间戳,请求前先算剩余寿命,把"用到死"改成"提前退休"。
验证标准:修复后连续跑 1 小时,统计 ConnectionRefused 与 TCPTimedOut 占比,相比修复前明显回落且不再成批出现,即为生效。
这一条最反直觉,值得多说两句。很多人看到日志里 Retrying 了好几次还是失败,第一反应是"重试次数不够,加大"。错了——Scrapy 内置 RetryMiddleware 默认重试 2 次,且重试请求沿用原 meta,代理字段原样保留,等于拿同一个失效 IP 再试两遍。 次数加到 10 次也只是失败得更慢。官方文档写得明白:超时、连接拒绝这类异常默认都在重试范围,但更换代理要开发者自己实现。
诊断方法:DEBUG 日志里找同一 URL 的多行 Retrying 记录,对比每行的 proxy 字段,地址一模一样即确认。
路径一,自己重写重试逻辑,在重试前更换 meta["proxy"]:
class ProxyRetryMiddleware(RetryMiddleware):
def process_exception(self, request, spider, exception=None, **kw):
request.meta["proxy"] = get_new_proxy()
return self._retry(request, exception, spider)路径二,把换 IP 交给云端。适合不想维护这段逻辑的团队。我的判断标准是:换 IP 代码越写越厚、自建 IP 池的维护本身成了新故障源,就该切隧道模式了。隧道代理统一入口接入,云端毫秒级自动换 IP、异常 IP 自动切换,程序端保留默认重试配置即可,每次重试天然走新出口。
验证标准:路径一用无效代理故意触发重试,日志中相邻两行 Retrying 的 proxy 字段应不同;路径二观察失败率,连接类异常不再随重试累积即为生效。
先纠正一个普遍误区:并发不是越大越快。 超过代理服务的额定吞吐后,多出来的请求只会堆进超时队列,表现为 ReadTimeout 占比一路走高,间歇触发目标站频控返回 429。
诊断动作:Scrapy 默认 CONCURRENT_REQUESTS 是 16,对照你所用套餐的额定请求数和带宽,两个都要看。拿我手上项目的真实配置说:我们用的极安隧道,基础套餐额定每秒 5 个请求、5M 带宽。带宽这个数顺便展开一下,当时选型我对比过几家,5M 的起步带宽在国内厂商里算给得大方的,不少家基础套餐明显更小,同样并发下先撞的往往是带宽墙,选型别只盯着 IP 数量和单价。但额定请求数摆在那,每秒 5 个的配置下把并发拉到 64 不会更快,只会更堵。我见过有团队一边开 64 并发一边投诉代理慢,对完额定吞吐才发现是自己把路堵死的。
解决三步:
CONCURRENT_REQUESTS 降到与额定吞吐对齐的量级;DOWNLOAD_DELAY 设 0.5-1 秒并开启随机化;验证标准:跑 30 分钟统计超时占比,回落到 5% 以内且 429 不再出现即为对齐。仍偏高就继续下调并发,注意方向是降并发,不是上调超时时间,后者只是把问题藏起来。
老手和新手最大的差别,不是会修多少问题,而是知道哪些问题不归自己修。三种情况继续调参数纯属浪费时间,判断标准就一条:问题是否还在代理接入层内。
其一,关掉代理直连也失败,或目标页面改版、接口下线。 问题在目标侧或解析层,换任何代理都无解。
其二,数据源需要登录授权或属于非公开数据。 这是合规边界,不是技术问题。我的立场很明确:停止采集,回到数据授权路径,任何配置手段都不该拿来处理边界问题,这条在团队里没有讨论空间。
其三,白名单确认无误、curl 直测代理仍大面积异常。 问题在服务端链路,该交给服务商定位。提工单别只甩一句"代理不稳定",带上这四项信息能最快定位:
信息项 | 作用 |
|---|---|
异常时间戳 | 对应服务端链路日志区间 |
报错日志片段 | 区分鉴权、连接、超时三类问题 |
接入方式 | 白名单或账密,排查路径不同 |
并发量与请求频率 | 判断是否为吞吐配置问题 |
自己修接入层内的问题,边界外的交给对的人,排查时间才花在刀刃上。
总结一下这套排障观:采集不稳定,先查配置错配,再怀疑代理质量,顺序别反。 五条原因按概率排好了,下次系统抖动,对着信号表走一遍,大概率不用换服务商。
评论区欢迎交流,尤其想听听大家在原因四上的实现:重写重试中间件的,有没有遇到过和其他中间件的执行顺序冲突?
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。