先说一个大多数写爬虫的人都遇到过的场面:脚本前几百个请求一切正常,忽然开始返回验证码页面,再往后直接连接超时——目标网站把你的本机IP限流了。上周帮朋友排查一个价格监控脚本,就是卡在第400多个请求上,最后靠挂代理解决。这篇文章不讲长篇原理,直接给requests的实战代码,从最基础的单代理设置,一路写到自动重试和Session方案,照着抄就能跑。
最基础的用法:一个proxies参数就够
requests对代理的支持非常直白,get或post里传一个proxies字典,请求就会从代理走。看最小可用的例子:
import requests
url = "https://httpbin.org/ip"
# 代理IP和端口,从服务商后台复制
proxies = {
"http": "http://114.80.32.12:4231",
"https": "http://114.80.32.12:4231"
}
resp = requests.get(url, proxies=proxies, timeout=10)
print(resp.json())
# 返回类似 {"origin": "114.80.32.12"},说明代理已生效
跑一下,如果打印出来的origin是代理的地址而不是你本机的宽带IP,说明代理已经生效,后面的请求都会从这个出口出去。
细节一:http和https两个键都要写。很多网站会做协议跳转,只配一个键,跳转之后的请求就绕过代理直连了,表现就是"明明挂了代理还是被限制"。
细节二:timeout必须设。代理质量参差不齐,不设超时的请求可能卡上几分钟,把整条采集流水线拖死。10秒是个合理的起点。
细节三:需要认证的代理格式不同。把账号密码写进地址里,格式是http://用户名:密码@ip:端口,其余写法不变。
代理IP从哪来:用API提取对接爬虫
正经项目靠手动复制IP肯定不行,主流做法是通过服务商的API提取接口,按需拉一批新IP下来。流程很简单:在服务商个人中心生成一条提取链接,requests直接请求它,返回的文本每行一个ip:端口,解析成列表就能用:
import requests
# 在服务商个人中心复制你的API提取链接
api_url = "你的提取链接"
def pull_proxies(num=10):
"""拉取一批代理IP,返回 ip:port 列表"""
params = {"num": num, "format": "txt"}
resp = requests.get(api_url, params=params, timeout=10)
resp.raise_for_status()
# 返回内容一般每行一个 ip:port
return [line.strip() for line in resp.text.splitlines() if line.strip()]
pool = pull_proxies()
print(f"本次拿到 {len(pool)} 个代理:")
for p in pool:
print(p)
选型上多说两句。网上能搜到不少免费代理列表,看着热闹,实测可用率经常不到三成,跑正式项目会把时间全耗在无效重试上。付费服务里我目前用的是神龙HTTP,看中两点:一是提取接口对requests、Scrapy这类主流爬虫工具开箱即用,文档里连示例代码都是现成的,对接花了不到十分钟;二是IP储备有3000万以上、可用率标称99.9%,池子够大,连续跑一晚上也很少出现整批失效的情况。
封装一个失败自动重试的请求函数
有了IP池,接下来把"挑代理、发请求、失败换下一个"这套动作封装成函数,业务代码只管调用,不用关心代理的细节:
import random
import time
import requests
def fetch_with_proxy(url, pool, max_retry=3):
"""带自动重试的请求:失败就换下一个代理"""
for i in range(max_retry):
proxy = random.choice(pool)
proxies = {
"http": f"http://{proxy}",
"https": f"http://{proxy}"
}
try:
resp = requests.get(url, proxies=proxies, timeout=10)
resp.raise_for_status()
return resp.text
except requests.RequestException as e:
print(f"第{i+1}次尝试,代理 {proxy} 失败:{type(e).__name__}")
time.sleep(1)
return None
html = fetch_with_proxy("https://httpbin.org/ip", pool)
print(html)
这个结构有三个旋钮可以按需调整:max_retry控制最多试几次;time.sleep的间隔看目标网站的承受能力,礼貌一点可以放到2到3秒;如果业务对地区有要求,提取IP的时候直接指定城市,拿到的池子就自带地区属性,神龙HTTP支持300多个城市的精准定位,在提取链接里加个参数就能实现。
Session加自动重试:长期项目这样跑
自己写重试循环没问题,但requests其实自带一套更省事的做法:Session配HTTPAdapter,把重试策略挂在会话上,一次配置全程生效。
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 带认证的代理写法:用户名:密码@ip:端口
proxies = {
"http": "http://用户名:密码@114.80.32.12:4231",
"https": "http://用户名:密码@114.80.32.12:4231"
}
session = requests.Session()
retry = Retry(
total=3, # 最多重试3次
backoff_factor=1, # 重试间隔逐次翻倍:1s、2s、4s
status_forcelist=[500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.proxies.update(proxies)
resp = session.get("https://httpbin.org/ip", timeout=10)
print(resp.json())
backoff_factor=1让重试间隔逐次翻倍,给临时性的网络抖动留出恢复时间;status_forcelist把500、502这些服务器端错误也纳入重试范围。把这段初始化放在脚本开头,后面所有请求自动带上代理和重试,业务代码干净很多。
顺带说说代理类型怎么配业务,我常用的对比是这样:
| 对比项 | 免费代理 | 短效动态代理 | 长效静态代理 |
|---|---|---|---|
| 可用率 | 波动大,常低于三成 | 99%以上 | 长期在线 |
| 速度 | 时快时慢 | 低延迟 | 稳定 |
| 存活时间 | 几分钟到几小时 | 按分钟计 | 天级到月级 |
| 适合场景 | 练手测试 | 大规模公开数据采集 | 需要固定出口的业务 |
一句话总结:高频请求、需要不断获取新IP的采集任务用短效动态;要固定出口、维持登录态的业务用长效静态。拿不准就先小规模各试一天,看成功率和速度哪个更符合预期。
四个我踩过的坑,帮你省点调试时间
坑一:只配了http键。很多页面会从http跳到https,proxies里只写一个键,跳转后的请求就裸奔了。两个键都写上,一劳永逸。
坑二:环境变量里藏着旧代理。requests会默认读取系统环境变量里的代理设置,如果你之前设过http_proxy,代码里就算不传proxies,请求也会悄悄走那条通道。排查"代理怎么不生效"这类问题时先检查环境变量,或者在Session里设trust_env=False。
坑三:证书报错就verify=False。个别代理节点会抛SSLError,先分清是节点的问题还是代码的问题,节点有问题直接找服务商换一个,别图省事关掉证书校验,采集数据的完整性会受影响。
坑四:提取接口被自己打爆。提取API一般有调用频率限制,开多线程跑的时候,先把IP一次拉够放进队列慢慢用,别每个请求都去调一次提取接口,否则被限流的是你自己。
把上面这些拼起来:API提取拿IP、封装好的请求函数负责重试、Session管会话,一套能长期稳定跑的采集骨架就有了。剩下的就是按业务量调整提取频率和并发数,边跑边观察成功率。
常见问题
Q:免费代理IP列表能用吗?
练手玩玩可以,正式项目别指望。免费代理可用率通常不到三成,速度也没保障,光是筛选有效IP的时间就够你把正经事做完。预算有限的话,可以先用按量计费的短效动态IP小规模试水,跑通了再加量。
Q:代理需要用户名密码认证,代码怎么写?
把认证信息直接拼进代理地址,格式是http://用户名:密码@ip:端口,http和https两个键都要写。注意密码里有特殊字符时要做URL编码,比如@要写成%40。
Q:设置了proxies,返回的还是本机IP怎么办?
按顺序查三件事:代理格式对不对(协议头不能少)、代理是不是已经过期失效、环境变量里有没有残留的旧设置。最快的验证方式是请求httpbin.org/ip,看返回的origin是不是代理的地址。
Q:短效动态IP和长效静态IP到底怎么选?
看业务需不需要固定出口。公开数据采集、需要频繁获取新IP的场景选短效动态;要维持登录状态、对接IP白名单的业务选长效静态。神龙HTTP两条产品线都有,个人中心里的可视化用量统计能看到每种类型的实时消耗,跑一段时间数据自然就告诉你答案了。






