Python Requests库所有代理请求失败但curl请求正常的问题排查求助
咱们来好好捋捋你的核心问题:同一批代理用curl能正常访问目标站点,但换成requests库发起请求时,要么连接被拒绝、要么超时,不管是HTTP/HTTPS代理还是SOCKS代理都出现了问题。结合你的代码、日志和包版本,我整理了几个最可能的原因和对应的排查方案:
1. 代理协议与请求协议不匹配
看你日志里第一个代理的配置:{'https': 'https://213.14.32.73:9090', 'http': 'https://213.14.32.73:9090'}——这里给http字段配置了HTTPS协议的代理,虽然部分代理服务器兼容这种跨协议配置,但有些严格的代理会直接拒绝这类请求。
修正方法:确保代理协议和请求协议一一对应:
# 如果代理同时支持HTTP和HTTPS,分开配置协议 proxies = { "https": "https://213.14.32.73:9090", "http": "http://213.14.32.73:9090" } # 要是代理只支持单协议,就只配置对应字段
2. SOCKS代理的依赖适配问题
你的日志里有socks4/socks5代理的超时错误,虽然你安装了PySocks 1.7.1,但Requests对SOCKS的支持需要正确的依赖关联。在Requests 2.x版本中,最好通过requests[socks]的方式安装,确保依赖被正确绑定。
修复步骤:
先卸载现有包,重新安装带SOCKS支持的Requests:
pipenv uninstall requests pysocks pipenv install requests[socks]
安装完成后再测试SOCKS代理的请求,应该能正常走PySocks通道了。
3. 请求头差异导致的校验拦截
curl和requests的默认请求头有明显差异,目标站点可能对请求头做了校验,直接拒绝了Requests的默认请求头(比如User-Agent字段)。
验证方法:把你测试用的curl请求头完全复制到Requests中,比如:
headers = { "User-Agent": "curl/7.81.0", # 替换成你实际curl的User-Agent "Accept": "*/*" } result = requests.get(url, proxies=proxies, timeout=3, headers=headers)
如果这样能成功,说明就是请求头的问题,后续只要调整Requests的默认请求头匹配curl即可。
4. 超时时间设置过短
看日志里有Read timed out. (read timeout=3),3秒的超时对于免费代理来说确实太严格了——免费代理通常延迟较高,而curl默认的超时时间比3秒长得多(甚至无限制),所以可能代理在3秒内没响应,但curl等得更久就成功了。
调整方法:适当延长超时时间,比如设置为10秒,或者分开配置连接超时和读取超时:
# 统一设置总超时为10秒 result = requests.get(url, proxies=proxies, timeout=10) # 分开设置:连接超时5秒,读取超时15秒 result = requests.get(url, proxies=proxies, timeout=(5, 15))
5. DNS解析方式的差异
Requests默认会在本地解析目标域名,再把IP发给代理;而curl默认是让代理服务器去解析域名。如果代理的DNS解析能力和本地不同,或者目标站点做了基于代理IP的DNS劫持,就会导致这种差异。
解决方法:让Requests通过代理来解析域名。对于HTTP/HTTPS代理,可以先通过curl获取目标站点的IP,然后用IP发起请求并加上Host头:
# 假设通过curl得到目标站点的IP是1.2.3.4 url = "https://1.2.3.4/inner/site/url?date=3day" headers = {"Host": "www.somehost.com"} # 注意:verify=False是因为IP和域名不匹配导致证书验证失败,生产环境需处理证书问题 result = requests.get(url, proxies=proxies, headers=headers, verify=False)
对于SOCKS代理,安装requests[socks]后默认就是代理解析域名,不用额外配置。
6. 免费代理的可用性波动
虽然你说随机测试了几个代理都正常,但免费代理的可用性波动极大——可能你用Requests请求的时候代理刚好失效,而curl测试时它还能工作。可以在代码里加入代理预检测的步骤:
def test_proxy(proxy_url, test_url="http://httpbin.org/ip"): try: proxies = {"http": proxy_url, "https": proxy_url} resp = requests.get(test_url, proxies=proxies, timeout=5) return resp.status_code == 200 except Exception as e: print(f"代理 {proxy_url} 不可用: {e}") return False # 使用代理前先检测 if test_proxy(next_ip): # 发起目标请求 result = requests.get(url, proxies={"https": next_ip, "http": next_ip}, timeout=10)
内容的提问来源于stack exchange,提问作者Andrey Kashirin

