前阵子给一个比价项目做采集,目标站会按访问者所在地返回不同价格。我明明把IP换成了成都的,抓回来的还是天津的价。查了两天代码没看出问题,最后抓包才发现:域名压根不是代理那边解析的,是我本机先解析成IP、再把IP丢给代理去连的。这篇就把DNS这一环单独讲一遍。
域名在哪解析,决定你落在哪个节点
大多数人调代理只看IP通不通,很少留意域名是在哪解析的。可目标站要是挂了CDN,解析出来的节点IP就直接决定了他认你在哪个城市。
我那次就是这样:本机在天津,DNS也是天津解析的,拿到的自然是最靠天津的节点。虽然我出口IP是成都的,但在目标站看来就成了"成都的IP去访问天津的节点",风控一比对就矛盾了——价格当然还是天津的。
socks5 和 socks5h 就差一个字母
这是最容易栽的地方。用 Python 的 requests 走 SOCKS5,地址写成 socks5:// 是本机解析,写成 socks5h:// 才是交给代理那边解析。多一个 h,行为完全反过来。我当时写的就是没有 h 的那个。
curl 一样,--socks5 是本机解析,--socks5-hostname 才是远程解析。这两个参数名起得不算直观,我第一遍翻文档就看露了。
浏览器这边更容易混过去
浏览器走 HTTP/HTTPS 代理的时候,连目标站之前会把域名整个交给代理(就是那个 CONNECT 请求),所以解析一般发生在代理侧,这部分不用操心。
换成 SOCKS5 代理就不一定了。Firefox 的代理设置里有一项"使用 SOCKS v5 时代理 DNS 查询",默认是勾上的;要是被关掉,或者你用的是命令行拉起来的无头浏览器、启动参数没配全,DNS 就会悄悄回到本机解析。我是用的无头浏览器,一开始完全没意识到这茬。
怎么确认到底在哪解析
我最后是三步确定的,第三步最省事,后来基本只用那一招。
第一步对比解析结果:本机 nslookup 一次目标域名,再用能远程解析的写法跑一次,两边解析出来的IP不一样,问题就基本定位在解析环节了。
第二步抓包:看 DNS 请求是不是从本机网卡发出去的。如果压根没看到 DNS 请求,说明是代理那边解析的,这一步最直观。
第三步,也是最省事的:打开目标站的"我的位置"或者"我的IP"页面。返回的城市跟你预期不一致,不用再往下查了,就是解析的问题。
| 场景 | 写法 | DNS 在哪解析 |
|---|---|---|
| requests + SOCKS5 | socks5h:// | 代理侧(推荐) |
| requests + SOCKS5 | socks5:// | 本机 |
| curl + SOCKS5 | --socks5-hostname | 代理侧 |
| 浏览器 + HTTP/HTTPS 代理 | 普通设置即可 | 代理侧(CONNECT 带域名) |
| 浏览器 / 无头浏览器 + SOCKS5 | 要勾"代理 DNS 查询" | 没勾就是本机 |
顺手发现的两个副作用
本机解析还有个坏处是慢。内网DNS不一定稳,偶尔卡个两三秒,而你的超时时间是算总时长的,表现出来就像"代理变慢了",其实是解析慢。我把解析改成走代理之后,同一批IP的平均耗时少了两百多毫秒——不是IP变快了,是少了一次本机等待。
另一个是解析污染。有些网络环境下公共DNS会返回一个奇怪的结果,页面能打开,内容却不对。改了远程解析之后这个问题也一起没了。
先说清楚什么时候不用折腾
如果你的目标站根本不看地域,比如只抓一个静态文档站,那就别为解析这事花时间,IP通就能干活。
反过来,只要目标站涉及比价、本地生活、区域限购这类业务,解析这一环就值得单独确认一次。我现在的做法是按目标站范围一次提20条进本地池,解析统一走代理侧,授权用白名单直连。星月代理支持API实时提取、短效IP自动轮换,池子覆盖全国300多个城市,可用率标的是90%以上,按上面这套跑下来比我原来省了一半以上的排查时间。
DNS 排查要点:先判断目标站认不认地域;脚本走 SOCKS5 要带 h(socks5h)或 curl 用 --socks5-hostname 才是远程解析;浏览器 SOCKS5 要确认"代理 DNS 查询"没被关掉;本机解析慢会伪装成代理慢;确认方式用目标站的"我的位置"页面最快。