别急着改代码,先搞清楚代理到底有没有被用上
半夜三点还在跟一段二十行的爬虫较劲,这事我干过不止一次。明明 proxies 参数写上了,代码也不报错,结果接口日志里看到的还是本机 IP。后来我学乖了:先分叉,再动手。
拿一个能回显出口 IP 的接口打一次,看返回的是什么。打印出来如果还是你自己的 IP,那就是代理压根没生效;如果显示的是代理那边的地址,那问题就不在 requests 上,而在目标站点识别、请求头或者频率控制这些地方。这两条路的处理方式完全不同,先分清能省掉一半时间。
import requests
proxy = "http://12.34.56.78:8080"
r = requests.get(
"https://ip-check.example.com", # 换成任意回显出口IP的接口
proxies={"http": proxy, "https": proxy},
timeout=8
)
print(r.text) # 打印的是谁,就知道谁在发请求
还有一种更直接的办法:把日志级别调到 DEBUG。urllib3 会老实告诉你它连的是哪个地址,那行 Starting new HTTP connection 后面跟着的 IP,永远不会骗人。
import logging
logging.basicConfig(level=logging.DEBUG)
看到的是本机地址,就回去查配置;看到的是代理地址,就往下看"通了但没用"那一节。
proxies 字典这几个写法,十个里有八个踩过
requests 的 proxies 参数看着简单,其实对格式挺挑。最常见的四种翻车姿势,我按踩坑频率排了个序。
第一种,值不带协议前缀。"12.34.56.78:8080" 这么写,requests 不知道该用什么协议去连,轻则报缺少协议头,重则连不上。正确格式是 "http://12.34.56.78:8080",前面的 http 是"用什么协议连代理",不是"访问什么协议的站点",这点很多人绕不过来。
第二种,只写了 http 一个键。字典里没有 https 这个键的时候,https 请求会走直连,而且不报错、不提示,最难查。所以两个键都写上,值可以完全一样。
第三种,用户名密码里有 @、#、: 这类字符没做编码。请求一头扎进错误的主机名里,报出来的错跟代理八竿子打不着。这个放到后面单独说。
第四种最冤——字典只写进了某一次 requests.get() 的调用里,换个函数、换个文件就又裸奔了。老项目里这种半截子配置特别多。
代码明明没问题,为什么还是直连
如果字典格式已经对了,还是走直连,八成是被"环境"截胡了。requests 默认会去读系统环境变量里的代理设置,包括 http_proxy、https_proxy,Windows 上还会读大写的 HTTP_PROXY。
这就带来两个坑。一个是你在代码里设的代理,被环境变量里的空值或者旧值顶掉了;另一个是公司电脑、容器镜像里预置了代理变量,本地跑得好好的代码换台机器就不对劲了。排查时先看一眼:
import urllib.request
print(urllib.request.getproxies()) # 有没有你没设过的代理冒出来
想让代码说了算,就把开关关掉:
s = requests.Session()
s.trust_env = False # 不读系统代理设置
s.proxies = {"http": proxy, "https": proxy} # 统一挂在会话上
r = s.get("http://t.example.com/data", timeout=8)
把代理挂在 Session 上还有个好处:一次设置,后面所有请求都生效,不用每个函数都写一遍。顺便提一句,no_proxy 里如果含了 127.0.0.1 或者某个域名,那部分请求也会绕过代理,这个细节经常被忽略。
另外,如果你用的是 socks5:// 开头的地址,记得先装依赖,否则会直接报缺依赖:
pip install requests[socks]
把报错信息当线索,比瞎改快得多
| 你写的 / 遇到的情况 | 实际会发生什么 | 改成 |
|---|---|---|
"12.34.56.78:8080" |
报缺少协议头,或直接连不上 | "http://12.34.56.78:8080" |
| 只写了 http 键 | https 请求静默直连,无任何提示 | http、https 两个键都写 |
socks5:// 未装依赖 |
Missing dependencies for SOCKS support | pip install requests[socks] |
密码含 @ # |
返回 407,或连到奇怪的主机上 | 用 quote 编码用户名和密码 |
参数名写成 proxy= |
TypeError,参数不被识别 | 改成 proxies= |
通了,但还是拿不到数据,问题在别处
这一档最容易被误判成"代理没设置好"。请求确实走了代理,只是对面站点把这次请求挡了回来。几个可以马上验证的点:
看状态码。407 是代理认证没过,账号密码或者授权方式有问题;403、429 是目标站点给的回执,说明代理本身是通的,问题在你的请求节奏或请求头。这两种情况的处理方向完全是相反的。
补全请求头。只有一条 User-Agent: python-requests/2.x 的请求,走什么代理都显眼。带上常规的 UA、Accept、Accept-Language,成功率会有肉眼可见的变化。
给足超时。timeout=2 这种设置配上链路稍长的代理,会频繁触发超时,看上去像代理挂了,其实只是没等够。一般 8 到 15 秒更稳。
控制节奏。同一个 IP 连着打几十次,再好的代理也扛不住。加个 time.sleep() 或者控制并发数,比换十个 IP 都管用。
用户名密码里的特殊字符,单独拎出来说
代理地址是拼成 URL 的,格式是 协议://用户名:密码@主机:端口。只要密码里出现 @、#、/、:,整串东西的解析就会错位——requests 会把 @ 后面那段当成主机名,然后报一个跟代理毫无关系的连接错误。
解决办法是先把用户名和密码各自编码一遍再拼:
from urllib.parse import quote
user = quote("user@corp", safe="")
pwd = quote("p@ss#2024", safe="")
proxy = f"http://{user}:{pwd}@12.34.56.78:8080"
proxies = {"http": proxy, "https": proxy}
safe="" 这个参数别漏,默认情况下 / 不会被编码,还是会出问题。这段代码我一般封装成一个 build_proxy() 函数,全项目调它,省得每处都手写。
上线前花六分钟,把这六项过一遍
写完之后别急着跑全量任务,按顺序核对一遍:字典里 http 和 https 两个键都在不在;认证信息有没有编码;socks5 依赖装没装;超时是不是给到了 8 秒以上;trust_env 有没有按预期设置;最后再用那个回显接口确认一次出口 IP。这六步走完,百分之九十的"代理不生效"都当场现原形。
有些问题不在代码,在代理本身
排查到最后一层,你会发现配置全对、请求头也齐,成功率还是上不去。这时候该怀疑的是 IP 质量——是不是大量 IP 早就被目标站标记过,是不是并发一上去就大面积超时。
做公开数据采集和 AI 大模型训练语料整理这类活,IP 池的干净程度比数量更关键。神龙HTTP 这边有几个数字值得关注:IP 纯度 99.8%、可用率 99.9%,这个组合意味着你拿到的大多数 IP 是真能直接用的,而不是请求十次错七次。产品线上短效动态、长效静态、固定IP代理都覆盖,支持 HTTP/HTTPS/SOCKS5,前面那些 proxies 写法可以直接套用。
另一个实际好用的点是300+ 城市级精准定位。做区域市场研究的时候,同一个接口在不同城市返回的列表内容是不一样的,需要换到对应城市的 IP 才能核对得上。这种场景下,能按城市取 IP 比"IP 数量多"实在得多。
至于集成,API 兼容主流爬虫语言,文档和示例代码都给全了;个人中心里有可视化的用量统计和实时监控,哪个时间段失败率飙了、哪批 IP 表现异常,一眼能看出来。技术团队 7×24 小时在线,遇到 407、超时这类卡壳的问题,直接问比自己摸索快。
常见问题
Q:proxies 设置了,但打印出来的还是本机 IP,最可能是什么原因?
先看字典里的键名和协议前缀。最常见的两种情况是值没写 http:// 前缀,以及只写了 http 键——访问 https 站点时就会静默直连。其次检查环境变量是否覆盖了代码设置,必要时加 trust_env = False。
Q:代理地址写的是 http:// 开头,能访问 https 的站点吗?
可以。这里的前缀说的是"用什么协议连代理服务器",跟目标站点的协议没关系。不过字典里 http 和 https 两个键都要写上,值填同一个代理地址就行,少写一个就会有一类请求绕过代理。
Q:同一份脚本,家里能跑,公司电脑上就直连,为什么?
大概率是环境变量在作怪。公司电脑或者容器镜像里常预置了 HTTP_PROXY、no_proxy 之类的变量,requests 默认会读。用 urllib.request.getproxies() 打印一下就能看到,确认后用 trust_env = False 让代码说了算。
Q:请求返回 407,是代理没配置好吗?
407 是代理认证失败,说明请求确实到代理那儿了,链路是通的。重点查用户名密码是否正确、有没有做 URL 编码(密码里的 @、# 必须编码),以及账号是否还有效、授权方式是否匹配。






