先看清楚:一条请求是怎么"绕"出去的
很多人第一次用代理,卡住的地方不是代码,而是脑子里那根线没接上——明明是我写的爬虫,为什么目标网站看到的是另一个IP?
把代理服务器想成一个跑腿的。你的爬虫不去亲自敲门,它把写好的请求(网址、请求头、参数)打包交给跑腿的,跑腿的拿着自己的身份去目标网站拿数据,拿到之后再原样递回你手上。目标网站的日志里,留下的是跑腿的那个IP,不是你的。这就是请求转发的全部含义,没有更玄的东西。
在代码层面,这件事轻得有点让人意外。用 Python 的 requests 举个例子:
import requests
ip = "http://账号:密码@代理服务器地址:端口"
proxies = {
"http": ip,
"https": ip,
}
r = requests.get("https://example.com/list", proxies=proxies, timeout=10)
print(r.status_code, len(r.text))
就多了 proxies 这一个参数。剩下的转发、握手、回传,都是代理服务器在干。
真正需要留意的是协议层。HTTP 代理只处理明文 HTTP;遇到 HTTPS,它靠 CONNECT 开一条隧道,把加密流量原样透传,所以它看不到你的内容,只看得到你去了哪;SOCKS5 更底层,几乎什么流量都能塞进去转发。这就是为什么有的采集任务换了个代理就通了,换另一个就报错——协议对不上。选代理前先确认它支持 HTTP / HTTPS / SOCKS5 里的哪一种,能省掉一大半排查时间。
轮换调度:什么时候换、换成哪一种
单个IP高频去敲同一个站,结果基本都是三个字:慢、拦、封。所以代理IP爬虫的核心动作不是"用代理",而是"换着用"。中间那个决定换谁、何时换的模块,就是调度器。
调度器本身不复杂,它就是一个带状态的队列。IP进池子,被取走,用完标记状态,超时回收,坏掉的拉黑冷却。难的是定策略。常见的三种走法,对应三种完全不同的业务:
逐请求轮换。每发一条请求就换一个IP,池子里排到谁用谁。适合列表页、详情页这类互相独立的页面,彼此不依赖登录态。
按时间窗轮换。一个IP用满 N 秒或 N 条请求再换。好处是同一个出口有连续性,某些站点对你的行为判断会宽松一些,不至于每次都是陌生面孔。
失败即换。不做固定节奏,只在出错那一瞬间切走。看起来很聪明,实际上是被动的——等你发现失败,请求已经浪费了,只能当兜底手段,不适合当主策略。
还有一个容易忽略的维度:地区。做本地生活类的市场研究,你需要的是某个城市当地的出口IP,而不是随便一个能通的IP。神龙HTTP 提供 300+ 城市级定位,这类需求可以直接按城市挑,不用自己在池子里猜。
IP类型也要和策略配套。挑错了,调度再精巧也白搭:
| 代理类型 | 换IP节奏 | 适合的场景 | 要留意的点 |
|---|---|---|---|
| 短效动态IP | 分钟级甚至更短,随取随换 | 公开数据采集、AI大模型训练语料抓取 | 别指望它保持会话,一次请求内用完就丢 |
| 长效静态IP | 数小时到数天不变 | 需要保持登录态或连续翻页的任务 | 节奏要放慢,别把一个IP往死里用 |
| 固定IP代理 | 基本不变 | 接口白名单、长期稳定的对接方 | 提前和对方报备IP,别临时更换 |
神龙HTTP 这三条线都有,短效动态、长效静态、固定IP代理一站配齐,切换的时候不用换一套接入方式,改动量很小。
失败重试:把异常当成常态来设计
写过一段时间爬虫的人都会形成一个共识:失败不是意外,失败是默认状态。一条链路跑一万次,出问题几百次太正常了。区别只在于,你的代码是崩掉,还是自己爬起来。
爬起来之前,先得分清哪些失败值得重试。这一步做错,后面全是无用功:
407 认证失败、代理地址连不上——这是IP本身的问题,重试一百次也没用,直接把这个IP丢进冷却队列。
429 请求过多、503 服务不可用——对方在限速,换一个出口IP再试往往就通了,值得重试。
200 但返回内容里带着验证页——最阴险的一种。状态码是绿的,内容是空的,不判断内容就会把垃圾数据存进库里。所以要加一条内容校验。
超时——看情况。偶发超时可以重试,连续超时说明这个IP的线路质量不行,也要换。
判断清楚之后,重试逻辑本身只需要三件事:换IP、等一会儿、设上限。下面这段是能直接抄去改的写法:
import time
import requests
def fetch(url, pool, max_retry=3):
for i in range(max_retry):
ip = pool.pick() # 从IP池里取一个出口
proxies = {"http": ip, "https": ip}
try:
r = requests.get(url, proxies=proxies, timeout=8)
if r.status_code == 200 and "验证" not in r.text[:500]:
return r.text
pool.cooldown(ip, 60) # 内容不对劲,这个IP先歇一分钟
except requests.RequestException:
pool.cooldown(ip, 30) # 连不上或超时,歇三十秒
time.sleep(2 ** i) # 第一次等1秒,第二次2秒,第三次4秒
return None
这段代码里有三个细节值得咬一下。
一是 pool.cooldown(),坏IP不是删掉,是关小黑屋。过一阵子放出来可能又好了,直接删除等于浪费库存。
二是 2 ** i 这个退避。第一次失败等1秒,第二次2秒,第三次4秒。别小看这一两秒,它在告诉对方"我不是在猛敲",对降低后续被拦的概率有实际帮助。
三是 max_retry。一定要有上限。见过太多脚本卡在一个永远失败的页面上无限循环,跑了一夜,一条数据没多。
当并发量上去之后,这套东西还得再往前一步:把IP池的健康状态可视化。哪些IP连续失败、哪个城市的库存快见底、今天命中率比昨天低了多少——这些数据如果不看,你就是在盲调参数。神龙HTTP 的个人中心里有可视化用量统计和实时监控,异常能识别出来,使用趋势也能看,调策略的时候心里有底,不用靠猜。
把三段接起来:一条完整链路的日常样子
实际跑起来的时候,这三段是咬在一起的,不是三个独立模块。
开工前先通过 API 提取一批IP进池。神龙HTTP 的提取接口返回结构化数据,粘进队列就能用,主流爬虫语言都能直接对接,文档里有现成的示例代码,不用自己琢磨参数格式。
跑起来之后,调度器按你定的策略发IP,请求经代理转发出去,响应回来先过一遍内容校验。通过就落库,不通过就触发重试分支,重试分支再回头找调度器要一个新IP。整个环是闭的,自己转,人只在旁边看监控。
如果这条链路跑得顺,你会注意到一个变化:单机跑几千条请求,出错率能压到很低,而且不再需要你半夜起来看它有没有卡死。稳定不是靠运气,是靠这三段各司其职。
补一句选型上的实话:链路设计得再好,池子不行照样白搭。3000万+正规授权代理IP储备、99.9% 可用率、99.8% IP纯度,这几个数决定了你的重试分支到底是被偶尔触发,还是天天触发。国内三大运营商正规授权,来源干净,这一点在对稳定性有要求的采集任务里,比省那点成本重要得多。
常见问题
Q:代理设置好了,但返回的还是我自己的IP,是怎么回事?
八成是协议写错了。requests 在遇到 https 网址时会去找 proxies["https"],如果你只填了 http 那一项,它会悄悄地直连出去,不报错。把两个键都填上同一个代理地址,再试一次。
Q:要不要每个请求都换IP?
看任务。互相独立的列表页、详情页,逐请求轮换最省心。但如果你的任务需要连续翻页、保持登录态,换成逐请求轮换反而会频繁掉线,这时候该用长效静态IP,让同一个出口撑完整段流程。
Q:重试次数设多少合适?
三次是个稳妥的起点。再往上加,收益很小,因为连续三次都失败的IP,第四次大概率还是失败,只会拖慢整个队列。与其加次数,不如把冷却和换IP做扎实。
Q:返回200但内容是空的,算成功吗?
不算。这是代理IP爬虫里最容易被忽略的坑——状态码骗人,内容不骗人。建议在解析之前加一道轻量校验,比如判断页面长度、检查有没有关键字段、看返回里有没有出现验证页的特征词。多写这几行,能挡掉大量脏数据入库。
Q:并发一高就大面积失败,问题出在哪?
先看池子库存够不够撑住这个并发,再看提取频率有没有跟上消耗速度。库存不足时,调度器会把同一个IP反复发给不同线程,等于自己把自己限速了。神龙HTTP 支持低延迟高并发提取,配合实时监控看库存曲线,这种情况能提前发现。






