为何http.client请求正常,requests却出现读取超时或403错误?
问题分析:requests请求超时/403,http.client可正常访问
问题现象
请求https://img.uefa.com/imgml/uefacom/uel/social/og-default.jpg时:
- 用
requests.get()无请求头返回403,添加请求头则触发读取超时 - 用
http.client、curl命令、浏览器均可正常获取200状态码响应
核心差异:requests与http.client的默认行为区别
requests基于urllib3封装,默认会做一些额外处理,和http.client的原生请求逻辑存在差异,这是问题的根源:
- 请求头默认配置不同
requests默认会添加Accept-Encoding: gzip, deflate, br(启用压缩),同时默认User-Agent为python-requests/x.x.x,这类标识容易被反爬规则识别返回403;而http.client默认不带这两个请求头,curl和浏览器则使用合法的客户端标识。部分服务器对压缩编码的处理存在兼容性问题,会导致请求卡住超时。 - 连接与重试机制差异
requests默认开启连接池和重试策略,而http.client是单次连接模式。如果服务器对重复连接有拦截,或者重试触发了反爬限制,就会出现超时或403。 - SSL/TLS上下文细节差异
requests的默认SSL上下文和http.client手动创建的ssl._create_unverified_context()在TLS版本协商、加密套件选择上可能存在区别,部分服务器会因这些细节拒绝连接。
针对性解决办法
方案1:模拟http.client的请求头配置
禁用压缩编码,设置合法的客户端标识:
import requests url = 'https://img.uefa.com/imgml/uefacom/uel/social/og-default.jpg' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', 'Accept-Encoding': None # 禁用压缩,和http.client保持一致 } try: response = requests.get(url, headers=headers, verify=False, timeout=10) response.raise_for_status() print(response.status_code) # 保存图片 with open('image.jpg', 'wb') as f: f.write(response.content) except requests.exceptions.RequestException as e: print("Error:", e)
方案2:关闭连接池与重试机制
强制单次连接,避免重试触发反爬:
import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry url = 'https://img.uefa.com/imgml/uefacom/uel/social/og-default.jpg' # 构建不重试的适配器 adapter = HTTPAdapter(max_retries=Retry(total=0, read=False, connect=False)) session = requests.Session() session.mount('https://', adapter) # 设置合法UA并禁用压缩 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36', 'Accept-Encoding': None } try: response = session.get(url, headers=headers, verify=False, timeout=10) response.raise_for_status() print(response.status_code) except requests.exceptions.RequestException as e: print("Error:", e)
方案3:复用http.client的SSL上下文
让requests使用和http.client完全一致的SSL配置:
import requests import ssl url = 'https://img.uefa.com/imgml/uefacom/uel/social/og-default.jpg' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' } # 使用和http.client相同的SSL上下文 ssl_context = ssl._create_unverified_context() try: response = requests.get(url, headers=headers, verify=False, timeout=10, ssl=ssl_context) response.raise_for_status() print(response.status_code) except requests.exceptions.RequestException as e: print("Error:", e)
内容的提问来源于stack exchange,提问作者Alexander Santos
相关产品推荐
相关产品推荐

