首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >2026 实测:Scrapy 项目接入代理 IP,哪些坑最容易导致采集不稳定?

2026 实测:Scrapy 项目接入代理 IP,哪些坑最容易导致采集不稳定?

原创
作者头像
三三有猫
发布2026-07-31 14:43:57
发布2026-07-31 14:43:57
10
举报

带采集团队这些年,听得最多的一句话是"这家代理不行,换一家试试"。每次我都让对方先别动,把日志发我看看——排查下来,大部分所谓的"代理质量问题"换谁家都会原样复现:中间件优先级写错、IP 过了存活期还在用、重试沿用失效 IP、并发超过代理吞吐,清一色是配置和机制的错配,跟服务商没关系。

这篇把我平时排障的完整套路整理出来:五类高发原因,每条给诊断方法、修复步骤和验证标准,按信号对号入座就行,不用通读。

先对号入座:你的不稳定属于哪一类

看异常信号直接跳到对应小节。下表按出现概率从高到低排,前两条通常能解决大部分问题:

异常信号

高发原因

对应小节

出口 IP 始终是本机 IP,代理像没配一样

中间件优先级写错,代理从未生效

原因一

日志出现 407 Proxy Authentication Required

鉴权未通过

原因二

同一 IP 先成功后大量 ConnectionRefused、TCPTimedOut

IP 已过存活期

原因三

日志里 Retrying 多次,最终仍失败

重试沿用同一个失效 IP

原因四

大面积 ReadTimeout,间歇出现 429

并发超过代理额定吞吐

原因五

多种信号同时出现的,按一到五的顺序排,别跳着查。

排查前,三样东西先备齐

我接手任何一个"采集不稳定"的案子,第一件事都是确认这三样在不在,缺一样后面的步骤就执行不下去:

  1. DEBUG 级日志。 settings.py 里设 LOG_LEVEL = "DEBUG" 并指定 LOG_FILE。只有 DEBUG 级别才会记录每个请求实际使用的 proxy 字段,这是后面所有判断的依据。没开 DEBUG 就来问"为什么代理不生效"的,等于蒙着眼排障。
  2. curl 最小复现。 curl 加 -x 参数可以脱离 Scrapy 单独测代理连通性,把框架配置问题和代理链路问题切开。这是排障里最重要的一刀:分不清这两层,能在错误的方向上耗一整天。
  3. 代理后台权限。 白名单管理和 IP 提取记录的查看权限,排鉴权和存活期问题都要用到。

三样备齐后,建一个只请求 IP 回显接口的测试 spider,作为全程的验证基准:返回哪个出口 IP,一目了然。

原因一:中间件优先级写错,代理压根没生效

这是我见过的头号问题,而且最冤——很多人代理配置本身没错,只是从来没被应用过。

诊断很简单:跑一遍测试 spider,返回的仍是本机 IP,就是它。

原理翻一眼 Scrapy 源码就明白:内置 HttpProxyMiddleware 的优先级是 750,并且它发现请求已设置 proxy 时会直接放行。所以自定义代理中间件必须排在 750 之前,写进 meta 的代理才会被应用。社区通行写法是 543:

代码语言:javascript
复制
DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.ProxyMiddleware": 543,
}

中间件里通过 request.meta["proxy"] 写入接入地址即可。

验证标准:重跑测试 spider,返回 IP 变为代理出口,同时 DEBUG 日志的请求行里出现 proxy 字段。两个条件都满足才算修复,只满足一个继续查。

原因二:407 鉴权失败,先切一刀再分头查

407 说明请求到了代理服务器,但鉴权没过。老规矩,先用 curl 切开两层:

代码语言:javascript
复制
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 过了存活期

同一个 IP 前几次成功、随后集中爆 ConnectionRefused 或 TCPTimedOut,这个模式太典型了,基本可以直接锁定存活期问题。

诊断动作:去代理后台调 IP 提取记录,对比失败请求的时间戳和该 IP 的提取时间。时间差超过所选存活档位,就是拿失效 IP 在发请求,代码再怎么改都没用。

修复的关键认知是:存活档位要和单批任务耗时对齐,剩余寿命撑不完一批页面的 IP 就该提前弃用,而不是等它报错。 选服务的时候我会优先看档位灵不灵活,像极安代理的短效产品把存活期做成 1-15 分钟五档可选,到期自动失效、按每日 IP 数计费,单页耗时长的任务选长档,高频轮换选短档,档位选错换档就行,不用换产品线。程序侧配合记录每个 IP 的提取时间戳,请求前先算剩余寿命,把"用到死"改成"提前退休"。

验证标准:修复后连续跑 1 小时,统计 ConnectionRefused 与 TCPTimedOut 占比,相比修复前明显回落且不再成批出现,即为生效。

原因四:重试形同虚设,默认机制不换 IP

这一条最反直觉,值得多说两句。很多人看到日志里 Retrying 了好几次还是失败,第一反应是"重试次数不够,加大"。错了——Scrapy 内置 RetryMiddleware 默认重试 2 次,且重试请求沿用原 meta,代理字段原样保留,等于拿同一个失效 IP 再试两遍。 次数加到 10 次也只是失败得更慢。官方文档写得明白:超时、连接拒绝这类异常默认都在重试范围,但更换代理要开发者自己实现。

诊断方法:DEBUG 日志里找同一 URL 的多行 Retrying 记录,对比每行的 proxy 字段,地址一模一样即确认。

路径一,自己重写重试逻辑,在重试前更换 meta["proxy"]

代码语言:javascript
复制
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 字段应不同;路径二观察失败率,连接类异常不再随重试累积即为生效。

原因五:大量超时加 429,并发压过了代理吞吐

先纠正一个普遍误区:并发不是越大越快。 超过代理服务的额定吞吐后,多出来的请求只会堆进超时队列,表现为 ReadTimeout 占比一路走高,间歇触发目标站频控返回 429。

诊断动作:Scrapy 默认 CONCURRENT_REQUESTS 是 16,对照你所用套餐的额定请求数和带宽,两个都要看。拿我手上项目的真实配置说:我们用的极安隧道,基础套餐额定每秒 5 个请求、5M 带宽。带宽这个数顺便展开一下,当时选型我对比过几家,5M 的起步带宽在国内厂商里算给得大方的,不少家基础套餐明显更小,同样并发下先撞的往往是带宽墙,选型别只盯着 IP 数量和单价。但额定请求数摆在那,每秒 5 个的配置下把并发拉到 64 不会更快,只会更堵。我见过有团队一边开 64 并发一边投诉代理慢,对完额定吞吐才发现是自己把路堵死的。

解决三步:

  1. CONCURRENT_REQUESTS 降到与额定吞吐对齐的量级;
  2. DOWNLOAD_DELAY 设 0.5-1 秒并开启随机化;
  3. 启用 AutoThrottle 自动限速,框架默认参数是初始延迟 5 秒、最大延迟 60 秒,让它在上限内自适应调速。

验证标准:跑 30 分钟统计超时占比,回落到 5% 以内且 429 不再出现即为对齐。仍偏高就继续下调并发,注意方向是降并发,不是上调超时时间,后者只是把问题藏起来。

最后:哪些情况别硬修

老手和新手最大的差别,不是会修多少问题,而是知道哪些问题不归自己修。三种情况继续调参数纯属浪费时间,判断标准就一条:问题是否还在代理接入层内。

其一,关掉代理直连也失败,或目标页面改版、接口下线。 问题在目标侧或解析层,换任何代理都无解。

其二,数据源需要登录授权或属于非公开数据。 这是合规边界,不是技术问题。我的立场很明确:停止采集,回到数据授权路径,任何配置手段都不该拿来处理边界问题,这条在团队里没有讨论空间。

其三,白名单确认无误、curl 直测代理仍大面积异常。 问题在服务端链路,该交给服务商定位。提工单别只甩一句"代理不稳定",带上这四项信息能最快定位:

信息项

作用

异常时间戳

对应服务端链路日志区间

报错日志片段

区分鉴权、连接、超时三类问题

接入方式

白名单或账密,排查路径不同

并发量与请求频率

判断是否为吞吐配置问题

自己修接入层内的问题,边界外的交给对的人,排查时间才花在刀刃上。


总结一下这套排障观:采集不稳定,先查配置错配,再怀疑代理质量,顺序别反。 五条原因按概率排好了,下次系统抖动,对着信号表走一遍,大概率不用换服务商。

评论区欢迎交流,尤其想听听大家在原因四上的实现:重写重试中间件的,有没有遇到过和其他中间件的执行顺序冲突?

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 先对号入座:你的不稳定属于哪一类
  • 排查前,三样东西先备齐
  • 原因一:中间件优先级写错,代理压根没生效
  • 原因二:407 鉴权失败,先切一刀再分头查
  • 原因三:间歇性连接失败,九成是 IP 过了存活期
  • 原因四:重试形同虚设,默认机制不换 IP
  • 原因五:大量超时加 429,并发压过了代理吞吐
  • 最后:哪些情况别硬修
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档