身边做SEO和竞品监控的同行,注册代理IP时问服务商的第一句话几乎都是“你们IP池多少个”。这个问题在2020年还算问到点上,现在基本问偏了——池子规模决定你能不能用,而会话粘性参数决定你用得准不准。上个月帮一家做量化资讯的团队改行情采集链路,我就在这个参数上连栽两次,第二次才想明白问题出在哪。
注册代理IP时,三个参数比IP池规模更该问
先说我自己的判断:池子只要过千万级,日常场景基本够用,真正会卡住你的是下面这三个。
- 认证方式:白名单、账密、API提取。白名单在弹性伸缩的环境里最容易出事,我有个跑排名监控的客户用云函数节点,白名单加了10个出口IP,第二天机器重建,全部请求401,业务方直接打电话过来。
- 轮换粒度:按请求换、按时长粘性、长效静态,三者的代价完全不同。
- 计费口径:按提取IP数、按流量、还是按天包干。动态代理现在市场价已经压到0.0022元/IP起,隧道代理大约16元/天起,看着便宜,但口径选错能让月账单翻三倍。
这三个参数不是注册完才能调的,多数服务商在开通时就写进了账号套餐,中途改要重新走流程。所以注册代理IP这个动作,本质上是在给后面的采集架构定调子。
行情接口为什么对IP切换这么敏感
股票和基金的快照接口是无状态的,参数里带个代码就能返回,理论上每次请求换一个IP最安全。但行情系统里还有分时、逐笔、资金流这类接口,它们往往要先拿一次 token,再带着 token 拉数据,token 绑定IP或者绑定时段会话。你在这中间换IP,token 立刻失效,接口返回的不是报错而是空字段——这点最阴,很多采集脚本只看HTTP状态码200就当作成功,结果入库一堆“-”。
另一个被忽略的点是限流维度。数据源侧通常按 IP+UA 组合做QPS控制,只换IP不换 UA 指纹,等于在同一个池子里打转,触发429的概率并不会下降。我在旅游比价那个项目里验证过,UA 不动的情况下,换IP对429的缓解只有18%左右。
实测:四种轮换粒度跑同一份行情快照
测试对象是一个快照+分时混合接口,串行200次加20并发混合,跑三天取均值。所有模式走同一家隧道代理入口,只改账号侧的粘性参数,避免引入变量。
| 轮换模式 | P50延迟 | P95延迟 | 请求成功率 | 数据完整率 | 日成本(10万次) |
|---|
| 每请求换IP | 178ms | 436ms | 71.3% | 68.9% | 22元 |
| 60秒粘性 | 94ms | 213ms | 98.6% | 98.1% | 16元 |
| 300秒粘性 | 89ms | 198ms | 99.2% | 99.0% | 16元 |
| 30分钟长效 | 87ms | 191ms | 99.4% | 92.3% | 31元 |
这里有个反直觉的结果:30分钟长效IP请求成功率最高,数据完整率反而最低。原因不复杂,长效IP上的token过了刷新周期没换,接口照样返回200,字段却是空的。这个坑我第二次才定位到,一开始还以为是代理转发丢了body。所以粘性会话不是越长越好,得和 token 有效期对齐,我最后把参数定在300秒。
验证脚本与四步排查清单
注册完代理IP别急着上生产,先跑一遍下面的脚本,它能同时打出延迟分位和失败率。这段代码用的是账密认证的隧道入口,你要是走API提取,把proxies换成本地提取池即可。
import requests, time, statistics
from requests.adapters import HTTPAdapter
PROXY = "http://user:pass@tunnel.example.com:8080" # 账号侧控制粘性TTL
URL = "https://quote.example.com/api/v1/snapshot?code=600519" # 脱敏后的行情接口
UA = "Mozilla/5.0 (compatible; QuoteBot/1.2)"
s = requests.Session()
s.mount("https://", HTTPAdapter(pool_connections=32, pool_maxsize=32))
lat, fail = [], 0
for i in range(200):
t0 = time.perf_counter()
try:
r = s.get(URL, proxies={"http": PROXY, "https": PROXY},
timeout=(3, 5), headers={"User-Agent": UA})
r.raise_for_status()
if not r.json().get("data"): # 200但字段为空,同样算失败
fail += 1
else:
lat.append((time.perf_counter() - t0) * 1000)
except Exception:
fail += 1
print(f"成功率={1-fail/200:.1%} P50={statistics.median(lat):.0f}ms "
f"P95={sorted(lat)[int(len(lat)*0.95)]:.0f}ms")
注意第14行那个空字段判断。我见过太多脚本只校验状态码,把残缺数据当成功入库,等到做回测才发现价格序列断了一截。排查顺序建议这样走:
- 大量401/403但出口IP确实是新的——先查认证方式,白名单环境确认出口IP没有漂移。
- 429频率高于预期——检查UA和Header指纹,别只盯着IP。
- 状态码200但字段为空——粘性会话中途被切走,token失效,把TTL压到token有效期以内。
- P95延迟突然翻倍——看是否落到跨省节点,或者隧道端口并发打满,单独加一个入口端口做隔离。
这套测下来,采购决策其实很清晰:池子过千万、协议齐全、延迟进10ms量级只是及格线,真正拉开差距的是粘性参数能不能按账号粒度自定义。我自己长期用的是蚂蚁代理(mayihttp.com)的隧道入口,它在这一项上支持秒级调整,对行情这类时序敏感的采集比较友好,不过如果你的场景是纯无状态快照,按请求轮换反而更省成本。