先区分两种”被封”
在排查之前先明确一件事:你遇到的是IP 被封,还是浏览器/客户端指纹被识别?两者的表现有时很像,但解决方向完全不同。
判断方法很简单:把同一个请求换成完全不同的出口 IP 再试一次。
- 换 IP 后恢复正常 → 是 IP 层的问题,继续往下看
- 换 IP 后依然被拦 → 是客户端层的问题(请求头、指纹、行为模式),换代理没用
下面的 7 条都属于第一类。
1. 单 IP 并发过高
这是最常见的原因。同一个出口 IP 在极短时间内发出几十个并发请求,在任何站点的访问日志里都是异常模式。
判断标准:把并发从 32 降到 4,成功率是否明显回升。如果回升,就是这个原因。
处理:降低单 IP 并发,用更多 IP 分散负载。注意降低并发不等于降低总吞吐——通过增加 IP 数量可以保持总吞吐不变。
2. 请求间隔过于规律
time.sleep(1) 看起来很温和,但一百个请求严格间隔一秒,在统计上是非常明显的机器特征。真实用户的访问间隔是随机分布的。
处理:加随机抖动,并且不要用均匀分布。
import random, time
# 均匀分布仍然过于规律,用带长尾的分布更接近真人
delay = random.lognormvariate(mu=0, sigma=0.8) # 多数在 0.5–3 秒,偶尔更长
time.sleep(min(delay, 15))
对分页采集,不要按顺序一路抓到底。打乱页面顺序,效果比调整间隔更好。
3. 缺少必要的请求头
只发一个 User-Agent 是不够的。一个真实浏览器请求通常还带 Accept、Accept-Language、Accept-Encoding、Referer、sec-ch-ua 系列头。缺失这些的组合本身就是特征。
session.headers.update({
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"Upgrade-Insecure-Requests": "1",
})
Referer 尤其要注意:直接请求详情页却不带任何来源,是很明显的直接访问。合理的做法是先访问列表页,再带着列表页地址去请求详情页。
4. 会话没有正确轮换
用粘性会话做登录态任务时很容易犯这个错:整个采集过程共用一个 SESSION_ID,几百个请求全部走同一个出口 IP,等于用住宅代理的价格买了单 IP 的效果。
判断标准:打印每个请求实际使用的出口 IP,看分布是否合理。如果几百个请求只有一两个 IP,配置有问题。
处理:按任务粒度分配会话。列表页用轮换池,登录态任务用固定会话,不要混用。
5. 请求模式与页面结构不匹配
只请求详情页、从不请求列表页和静态资源;或者所有请求都在毫秒级完成;或者只抓某一个字段却下载了整个页面。这些都会形成异常画像。
处理:让请求序列更接近真实浏览路径。对关键任务,适当请求列表页和必要的资源,流量成本的小幅增加换来的是成功率的明显提升。
6. 目标站点的风控策略更新了
如果昨天还好好的,今天成功率突然腰斩,大概率不是你改了代码,而是对方调整了策略。这类变化通常表现为:同样的请求开始返回挑战页,或者返回 200 但内容为空。
处理:建立成功率监控,按小时或按天记录。发现突变时先确认是否只有自己在受影响(换一个完全不同的网络环境试同一个页面),再判断是否需要调整方案。
# 最小的成功率监控:按响应类型分类计数
from collections import Counter
stats = Counter()
def record(resp):
body = resp.text[:4000].lower()
if resp.status_code in (403, 429):
stats["blocked"] += 1
elif "captcha" in body or "验证码" in body:
stats["challenged"] += 1
else:
stats["ok"] += 1
# 结束时打印,成功率低于阈值就告警
total = sum(stats.values())
print(dict(stats), f"success={stats['ok']/max(total,1):.1%}")
重点是把 challenged 单独计数。它常被忽略,因为状态码是 200,程序会当成成功,直到你发现入库的数据量对不上。
7. 目标站点的数据本身就有地域限制
有些内容只在特定国家可访问,用其他地区的出口 IP 会直接被拒或返回占位内容。这种情况下无论如何调优都没用,需要的是正确的出口地区,而不是更多的 IP。
判断标准:用目标国家的出口 IP 手动访问一次,看内容是否不同。
处理:在请求层面按目标地区选择出口,确保代理配置的地区与业务目标一致。
排查顺序
按下面顺序做,能最快定位:
- 换一个完全不同的 IP 重试 —— 区分 IP 层和客户端层
- 打印每个请求的实际出口 IP —— 确认代理是否按预期工作
- 把并发降到 4 —— 排除并发问题
- 加上完整的请求头 —— 排除请求头缺失
- 加随机间隔并打乱顺序 —— 排除规律性
- 检查目标内容是否有地域差异 —— 排除地区限制
- 建立成功率监控 —— 区分”你的问题”和”对方变了”
前两步能覆盖八成情况。剩下的大多数是第 6 条,需要换出口地区而不是换供应商。
取舍结论
| 维度 | 结论 |
|---|---|
| 前提条件 | 已确认换用完全不同的出口 IP 后依然被拦,说明问题在客户端层而非 IP 层。 |
| 核心取舍 | 降低单 IP 并发、增加随机间隔都会牺牲吞吐,需要靠增加 IP 数量而不是提高单点压力来补回。 |
| 不适用场景 | 目标内容本身有地域限制时,任何请求模式优化都无效,必须换出口地区。 |
一句话:“换 IP 对照测试”和”打印实际出口 IP”这两步能覆盖八成排查场景。
在你的目标站点上验证这套方案
文中的做法都可以用试用额度直接跑通。用你自己的目标站点测一轮,成功率数据比任何评测都可靠。