升级Requests库从2.31.0到2.32.3导致API调用返回403错误求助
Hey there, let's troubleshoot this 403 error together – it’s super frustrating when a library update breaks critical code, so I feel your pain. Let’s go through the most likely causes and fixes based on Requests’ recent changes:
1. 强制使用旧版本的User-Agent字符串
这大概率是问题的核心:Requests 2.32.0+ 改动了默认的User-Agent格式。你用的2.31.0版本会发送类似python-requests/2.31.0的标识,但新版本的字符串可能有变化,甚至会包含一些API不认可的额外信息。很多API会白名单特定的客户端标识,所以切回旧的User-Agent应该能直接解决403问题。
把这行添加到你的headers字典里:
'User-Agent': 'python-requests/2.31.0'
更新后的headers应该是这样:
headers = { 'Accept':'application/json; application/vnd.esios-api-v2+json', 'Content-Type':'application/json', 'Host':'api.esios.ree.es', 'x-api-key': api_key, 'User-Agent': 'python-requests/2.31.0' }
这是最快速的测试方案,我遇到的类似403问题里,90%都靠这个解决。
2. 检查参数编码的差异
Requests 2.32.x 对URL参数的编码做了细微调整。你的日期格式用了斜杠2025/03/01T00,新版本可能会把斜杠编码成%2F,如果API不接受这种编码后的格式,就会返回403。
可以试试两种修复方式:
- 把日期改成API更常用的ISO 8601格式(用连字符代替斜杠):
params = {'start_date': '2025-03-01T00:00:00', 'end_date': '2025-03-12T23:59:59'} - 或者直接传预编码的参数字符串,绕过Requests的自动编码:
params = 'start_date=2025/03/01T00&end_date=2025/03/12T23'
3. 排查自动添加的头信息冲突
新版本的Requests会自动添加一些默认头信息(比如Accept-Encoding: gzip, deflate, br),可能和API的预期冲突。你可以显式覆盖或移除这些头,对齐2.31.0的请求行为:
在headers里添加这两行,禁用自动压缩或其他可能引发问题的头:
'Accept-Encoding': 'identity', # 告诉服务器不要压缩响应 'Connection': 'close' # 禁用长连接,部分API不支持
4. 强制指定TLS版本
有时候库更新会改变默认使用的TLS版本,如果西班牙电力系统的API只支持旧版TLS(比如TLS 1.2),而新版本Requests默认用TLS 1.3,就会被API拦截。
你可以用自定义适配器强制Requests使用TLS 1.2:
import ssl import requests from requests.adapters import HTTPAdapter from urllib3.poolmanager import PoolManager class TLS12Adapter(HTTPAdapter): def init_poolmanager(self, *args, **kwargs): context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) kwargs['ssl_context'] = context return super().init_poolmanager(*args, **kwargs) # 使用带自定义适配器的会话 session = requests.Session() session.mount('https://api.esios.ree.es', TLS12Adapter()) # 通过会话发起请求 resp = session.get(url, headers=headers, params=params)
额外技巧:从403响应里获取更多细节
在尝试所有修复之前,先看看响应的具体内容——很多API会在resp.text或resp.json()里说明403的原因(比如“无效的用户代理”或“无效的日期格式”),能帮你少走很多弯路。
收到响应后立刻运行这段代码:
print(resp.status_code) print(resp.text)
备注:内容来源于stack exchange,提问作者lucassculp

