上周一个做广告验证的老同学找我喝咖啡,开口就是:"我们Java采集端一天要发50万次请求,覆盖30个城市的广告展示效果,代理换到第四家,某几个城市的验证结果还是对不上,报表没法交。"
我干的活是反爬,坐的正好是他们的对面。这话我听着太耳熟了——那些被他们抱怨"不准"的IP,很多就是从我这边放过去、但只返回模板数据的"水货"。所以我干脆反向写一篇:从封禁日志的视角,拆一拆Java代理IP到底该怎么选。
先确认难点:城市级验证卡在哪三个约束上
广告验证的诉求是"我看到的就是当地用户看到的",听起来简单,落到工程上有三个硬约束,缺一个结果就废:
- 地域精度:不是国别,是城市,而且得落到运营商的城域网出口级别
- 并发:50万次/天,按10小时窗口摊开是每秒14次,峰值能冲到60次
- 会话一致性:同一个广告位要连续取3到5次样本再取中位数,中途跳一次城市,整组数据作废
他们第一版用的是某平台的机房IP,单价压到了0.001元/IP以内,看着很香。跑满一周后我帮他们统计:二线城市的城市命中率只有62%,一线勉强71%。问题不在采不采得到,而在采到的到底是不是那个城市的网。
坐在封禁那一侧:我们靠什么给代理打分
我们反爬系统给每个进站IP打分,权重最高的不是请求频率,是ASN聚集度。一个C段(256个IP)里若10个以上IP在5分钟内打同一接口,整段降权。机房IP的短板就在这里——很多供应商号称覆盖365城,实际在三线城市是用一两个骨干出口做"城市映射",几十个所谓城市IP共享同几个C段。你请求头里写着那个城市,ASN和TLS出口却把你卖了。
第二是TLS指纹和请求头时序。这一块和代理供应商关系不大,和Java端实现关系很大,我放到最后单独讲。第三才是频率——说实话,频率是最容易被绕过的一层,前两层过不去,频率调得再慢也白搭。
四种接入方式的实测对照
我帮他们搭了一套评估脚本,同一批30个城市、每城200次请求,用只回显客户端IP和ASN的探针接口做校验,跑出来的数据如下:
| 接入方式 | 平均延迟 | 城市命中率 | 可用率 | 适配场景 |
|---|
| 免费代理列表 | 850ms | 23% | 21% | 不建议 |
| 机房IP·API提取 | 42ms | 62% | 97.0% | 全国性、低精度任务 |
| 隧道代理·纯轮换 | <10ms | 88% | 99.5% | 高并发、无会话要求 |
| 隧道代理·粘性会话 | <10ms | 99.1% | 99.9% | 广告验证、多步采样 |
城市命中率的判定方式是:探针接口返回的ASN归属地与请求目标城市做比对,一致才算命中,不看供应商自己标的那行字。
坦白说我一开始押的是API提取,觉得按城市参数精确调取最可控。跑完第一轮才发现反了——提取出来的IP虽然标着城市,但实际出口复用严重;反而是隧道加粘性会话的组合最稳,因为它天然保证了一个会话内的出口不变。这个结论我改了两版脚本才认。
Java端的三个坑,比换供应商更影响结果
坑一:连接池会把粘性会话吃掉
Java的OkHttp和HttpClient默认开连接池。隧道代理的每一条TCP连接对应一个固定出口IP,连接一被复用,你在header里换会话ID也白搭,采样还是会跳到别的城市。跑城市轮换时,要么显式带上 Connection: close,要么把连接池压到极小:
OkHttpClient client = new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("tunnel.host", 8080)))
.proxyAuthenticator((route, resp) -> resp.request().newBuilder()
.header("Proxy-Authorization", Credentials.basic("user", "pass")).build())
.connectionPool(new ConnectionPool(2, 1, TimeUnit.SECONDS))
.connectTimeout(3, TimeUnit.SECONDS)
.readTimeout(8, TimeUnit.SECONDS)
.build();
坑二:超时参数设反了
很多人把connectTimeout设10s、readTimeout设30s。广告验证的响应体通常只有几KB,连接建得慢本身就是隧道质量差的信号,应该快速失败换一条,而不是耗在那等。我给的是连接3秒、读取8秒、失败最多重试2次,整体成功率反而比宽松超时高了4个百分点。
坑三:以为加了城市header就万事大吉
有些平台支持在请求头里指定城市,但实际出口是不是那个城市,得自己验。方法很简单:先用探针接口确认出口ASN,再开始正式采集,别把校验逻辑省掉——我见过有人省了这步,跑了三天才发现半个省的数据全是错的。
最终他们切到隧道代理加粘性会话的方案,单IP成本折算下来比原来贵了三成左右,但城市命中率从62%拉到99.1%,报表能交了。按我的经验,动态代理0.0022元/IP起、隧道16元/天这个价位段已经能覆盖绝大多数城市级验证需求——我自己长期用的是 mayihttp.com 这一档,3000万+IP池、365+城市覆盖,隧道模式的延迟稳定在10ms以内,接入上API提取、账密认证、白名单三种都支持,城市轮换场景直接用账密+隧道最省事。
最后给个判断:如果你的广告验证只看国别层级,机房API提取够用;一旦要精确到城市,别在IP单价上抠钱,把预算花在隧道和会话控制上,Java端再按上面的三个坑调一遍,回报率比换供应商高得多。