这是我最初那版采集脚本的核心,看起来没什么毛病:
PROXY = "http://user:pwd@tunnel.xxx.com:8000"
s = requests.Session()
s.proxies = {"http": PROXY, "https": PROXY}
for p in range(1, 300):
r = s.get(f"https://job-site-a.com/list?p={p}", timeout=8)
parse(r.text)
跑到第 40 页左右,A 站开始返回 403,B 站直接限频,200 个并发线程里有 180 个卡在等超时。那天下午我盯着终端刷了快两个小时日志,才反应过来问题不在代码,在出口 IP。
一、需求拆解:招聘站点的反爬,卡的是行为不是 IP
先说我的底子。工作室原本做游戏多开,手上 20 多个账号,用代理 IP 主要是防同 IP 关联封号。后来接了招聘岗位采集的活,才发现这两个场景取 IP 的逻辑完全不是一回事。
游戏多开要的是「长期稳定、一个账号一个固定出口」;招聘采集要的是「高频轮换、每次都是新面孔」。而三个目标站点的风控强度差得很远:
- A 站:登录态 + 页面停留 + IP 维度限频,三件套齐全
- B 站:纯 IP 限频,单 IP 60 秒内请求超过 20 次,封 10 分钟
- 地方人才网:基本裸奔,但岗位字段缺失率接近 30%,得靠其他源补
真正卡死我的是 A 站。它不看你 IP 池多大,它看同一个 IP 的行为序列像不像人——这也是我后来重建整个调度层的起点。
二、选型:IP 池规模是最不该看的指标
我第一反应是堆量。先买了个 5 万 IP 的套餐,机房段的,结果 A 站整段识别,第一天封禁率 31%。当时我以为换个更大的池子就能糊过去,跑了一周才明白:机房 IP 的 ASN 段早被拉进黑名单,池子再大也是同一批「有前科」的地址。
| 方案 | 单价 | 实测可用率 | A站封禁率 | 结论 |
|---|
| 公开免费代理 | 0 元 | 14.2% | 100% | 直接淘汰 |
| 机房动态 IP | 0.0015 元/IP | 92.4% | 31.6% | 只跑人才网 |
| 住宅动态 IP | 0.0022 元/IP 起 | 99.2% | 3.5% | A 站主力 |
| 隧道代理 | 16 元/天起 | 99.9% | 4.1% | 兜底长跑 |
表里后两档的报价我用的是蚂蚁代理,额度小先充了个 100 块试水,跑了三天才敢把主力流量切过去。选型上我的判断很明确:招聘这类中高风控站点,只看 IP 池规模没有意义,看得是住宅段占比和单 IP 复用上限。
三、调度层:三个参数决定封禁率
IP 选对了,脚本照样被封——因为调度层是另一回事。我把这块拆成了三步:
- 单 IP 请求上限 = 站点阈值 × 0.6。B 站 60 秒允许 20 次,我设成 12 次就强制换出口,留 40% 余量。
- 会话保持按请求数算,不按时间算。我一开始设了 300 秒粘性,200 并发下同一个出口 IP 被复用四百多次,这个坑我踩了两回才改过来。
- 超时从 8 秒压到 3 秒,失败不重试同一 IP,直接换下一个出口再打。
再补一个不太常见但很省事的做法:把 UA 和 Cookie 跟出口 IP 绑定,同一个 IP 在生命周期内始终用同一套指纹。A 站那套行为模型对「IP 换了指纹没换」很敏感,绑上之后异常请求率又降了一截。
四、实测:封禁率从 31% 到 1.2%
改造完成后我连续跑了 7 天,采集量稳定在 30 万条/天,各项指标对比:
| 指标 | 改造前 | 改造后 |
|---|
| A 站封禁率 | 31.6% | 1.2% |
| 平均响应延迟 | 240 ms | 8 ms |
| 30 万条耗时 | 11 小时 | 3 小时 20 分 |
| 单条 IP 成本 | 0.0061 元 | 0.0019 元 |
| 整体可用率 | 87.1% | 99.9% |
有个数我得老实交代:日均请求超过 800 万之后,住宅 IP 池的调度延迟会不会劣化,我没实测过。目前 30 万条/天这个量级,这套配置够用,再往上你可能得考虑分城市多节点调度。
回到最开始那个 403。并发采集 IP 不够用,八成不是池子小,是你把代理 IP 池当成水库在用了——它更像一条流水线,每个 IP 干多少活、干完怎么下工,比池子里存着多少水重要得多。选型那步要是还想省事,直接按住宅段占比去筛,比对着「几千万 IP」的宣传页纠结有用。