hqbsh.com 运行时间
HQBSH.com的whois记录显示注册于2013年1月18日,至今已经持续运营了:0年0个月0天零0小时0分钟0秒

最新报价
 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 10|回复: 0

requests 请求被大模型 API 拦截的 pretext 排查

[复制链接]

245

主题

1

回帖

127

银子

超级版主

积分
5254
发表于 2026-6-22 07:04 | 显示全部楼层 |阅读模式
[sessions/store] pruned stale session entries

用 `requests` 调 GPT、Claude、Kimi 这些大模型 API,偶尔会碰到 `403 Forbidden`、`429 Rate Limit`,甚至直接给你回个空响应。奇怪的是,同一套 API key,curl 命令在终端里跑得飞起,换成 Python requests 就各种报错。这种情况大概率是 pretext 配置的问题——人家把你当成非浏览器客户端了。

## 先说现象

```python
import requests

response = requests.post(
    "https://api.openai.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={"model": "gpt-4", "messages": [{"role": "user", "content": "hello"}]}
)
print(response.status_code)  # 403
print(response.text)         # {"error": {"message": "Invalid API key", ...}}
```

API key 明明没写错,curl 能通,就 Python 报错。你说气不气人。

## 四个常见坑

### 1. User-Agent 太明显

大模型 API 会校验请求头。`requests` 默认的 UA 形如 `python-requests/2.31.0`,这串字符在很多 WAF 眼里基本等同于"爬虫"或"未授权脚本"。

Cloudflare、AWS Shield、Azure Application Gateway 这些主流 WAF 服务,默认规则集里都有一条"识别非标准 HTTP 客户端"的检测项,专门针对不带浏览器特征的请求。你用 `requests` 默认配置发请求,在服务端看来就是一个人畜无害的 `python-requests/x.x.x`。

有个有意思的坑:即便 API key 完全正确,有些服务返回的错误信息会故意写成 "Invalid API key",而不是 "Forbidden" 或 "Blocked",目的就是让你误以为是 key 本身的问题,多浪费点排查时间。

### 2. 必要请求头缺失

没有 `Content-Type: application/json`,或缺少 `Accept` 等字段,服务端可能返回 422 或直接空 body。

大模型 API 普遍要求请求体是 JSON 格式,不声明 `Content-Type: application/json` 的话,有些服务会假设你提交的是表单编码或纯文本,直接返回 422 Unprocessable Entity。

`Accept` 头的作用是告诉服务端你能接受什么格式的响应。缺少这个字段,有些 API 会返回 XML 或纯文本,而不是你期望的 JSON。

另外,有些 API 还会检查 `Origin` 和 `Referer` 头。OpenAI 的 API 虽然不强制,但 Azure OpenAI Service 会校验 `Origin`。如果在浏览器环境里通过前端调用 Azure OpenAI,但发请求时没带正确的 Origin,Azure 会直接拒绝。

### 3. Session 复用埋的雷

```python
s = requests.Session()
s.headers.update({"Authorization": f"Bearer {API_KEY}"})  # 全局埋进去了
s.headers.update({"User-Agent": "Mozilla/5.0"})           # 覆盖掉

```

这是新手容易踩的坑,也是调试时最难发现的。`Session` 对象的 `headers` 是个 `CaseInsensitiveDict`,用 `.update()` 批量更新时,如果有重复的 keys,会直接覆盖。

更隐蔽的问题:你在 Session 级别埋了 `Authorization`,然后某次请求里单独传了 `headers={"Content-Type": "application/json"}`,requests 内部会把 Session 的 headers 和单次请求的 headers 合并——单次请求的优先级更高,所以单次请求里没传的字段会沿用 Session 的。但如果你某次请求里显式传了空的 `"User-Agent": ""`,Session 里的 UA 反而会被清空而不是保留。

还有个经典陷阱:你在 Session 级别更新了 headers,后续某个函数里用 `del s.headers["User-Agent"]` 删掉了这个字段,但你以为它还在。结果下一个请求没 UA,服务端直接拦截。

### 4. 代理在搞鬼

公司网络走代理时,`requests` 默认不走系统代理,导致请求无法发出或响应异常。

这个在企业内网环境里特别常见。本地开发机没挂代理一切正常,代码部署到内网服务器就挂了——因为那台服务器流量必须经过代理出口。

有意思的是,有时候走代理反而更容易被识别。代理服务器会在请求里加上 `X-Forwarded-For`、`X-Real-IP` 等字段,大模型 API 会检查这些字段的合理性。如果发现 IP 段是已知的代理出口段,直接降权或拒绝。

还有一种情况是代理把 TLS 证书重新加密了,导致证书链不完整。服务端校验失败时,`requests` 会报 SSLError,但有时候代理服务器会吞掉这个错误,返回一个空的 200 响应——这种最难排查。

## 怎么解决

### 第一步:用 curl 复现成功请求

先把成功请求的完整 curl 命令打出来:

```bash
curl -v -X POST https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4","messages":[{"role":"user","content":"hello"}]}'
```

从 `-v` 输出里把请求头完整抄下来,特别注意这几行:
- `> User-Agent:` 你在 curl 里写的 UA
- `> Accept:` curl 默认发的是 `*/*`,但大模型 API 可能需要显式指定 `application/json`
- `> Content-Type:` 你传的 JSON 类型

curl 默认会带上的 headers 远比你以为的少——它几乎是最简的 HTTP 客户端。很多时候 curl 能通但 requests 不行,差就差在 curl 的极简配置恰好避开了某些检测规则,而 requests 默认行为反而触发了检测。

### 第二步:照着抄请求头

```python
import requests

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
    # 从 curl -v 输出里照抄全部 headers
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
}

response = requests.post(
    "https://api.openai.com/v1/chat/completions",
    headers=headers,
    json={"model": "gpt-4", "messages": [{"role": "user", "content": "hello"}]},
    timeout=30
)
print(response.status_code, response.json())
```

UA 字符串尽量写完整。服务端不只检查 UA 字符串里有没有 "Mozilla",有些还会解析 UA 里的版本号、平台信息、渲染引擎字段是否完整、是否符合正常浏览器的特征组合。`Chrome/120.0.0.0` 这种格式比 `Chrome/120` 更真实。

### 第三步:Session 的正确打开方式

需要复用 Session 的话,别用 `.update()` 整体覆盖,改成逐字段设置:

```python
s = requests.Session()
s.headers["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
s.headers["Accept"] = "application/json"
s.headers["Content-Type"] = "application/json"
```

更稳妥的做法是继承 `requests.Session` 写一个子类,在 `__init__` 里只设置"客户端级"的 headers,把业务相关的 headers 全部放在每次请求时单独传:

```python
class MySession(requests.Session):
    def __init__(self, ua=None):
        super().__init__()
        self.headers["User-Agent"] = ua or "Mozilla/5.0 ..."
        self.headers["Accept"] = "application/json"

session = MySession()
resp = session.post(url, headers={"Authorization": f"Bearer {API_KEY}"}, json=data)
```

这样做的好处是,Authorization key 永远不会因为你忘记在某次请求里显式传入而被 Session 里的旧值污染,也不会有泄露风险。

### 第四步:代理配置

确认是否需要走代理:

```python
proxies = {
    "http": "http://proxy.example.com:8080",
    "https": "http://proxy.example.com:8080"
}

response = requests.post(
    url,
    headers=headers,
    json=payload,
    proxies=proxies,
    timeout=30
)
```

或者直接设环境变量让 requests 自动读取:

```bash
export HTTP_PROXY="http://proxy.example.com:8080"
export HTTPS_PROXY="http://proxy.example.com:8080"
```

不确定代理有没有在干扰的话,先用 `curl` 的 `--noproxy` 选项禁掉代理,对比一下结果:

```bash
curl --noproxy '*' -X POST https://api.openai.com/...
```

加上 `--noproxy` 之后 curl 能通但 requests 不行,那问题很可能就在代理配置上。

### 第五步:让 `raise_for_status()` 帮你挖坑

不要只检查状态码,有些 API 返回 200 但 body 里藏着错误:

```python
response = requests.post(url, headers=headers, json=payload, timeout=30)
try:
    response.raise_for_status()  # 4xx/5xx 抛异常
    data = response.json()
except requests.HTTPError as e:
    print(f"HTTP error: {e}")
    print(f"Response body: {response.text}")  # 打印完整响应,定位根因
```

OpenAI 的 API 在 token 耗尽时返回的状态码是 200,但 body 里会有 `"error": {"code": "insufficient_quota", ...}`。如果代码只检查 `status_code`,会以为请求成功然后尝试解析 body,结果报 JSON 解析错误。正确做法是先调用 `raise_for_status()`,它会把这类藏在 body 里的 HTTP 错误也转换成异常。

## 常见疑问

**Q: 如何优化 requests 请求的内存占用?**

三个方向:缩小上下文或缓存大小、限制并发数与队列长度、定期清理会话释放对象。配合持续压测观察是否有泄漏趋势。

**Q: requests 和同类方案相比差异在哪?**

主要看三项:运行时与依赖复杂度、资源占用曲线(空闲/峰值/回收)、以及生产可观测性(日志/指标/追踪)。

**Q: 有什么安全合规要注意的?**

密钥和令牌遵循最小权限原则,敏感数据不落盘或脱敏,外部调用走白名单并保留审计日志。生产环境建议开启严格的权限隔离。

---

说白了,大模型 API 对请求头的校验比传统 HTTP 接口严格得多。遇到 403/429 时,按这个优先级排查:UA → 请求头缺失 → 代理 → 频率限制。前两项解决掉,基本能跑通。

有什么踩坑经历,欢迎评论区聊聊。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

 
 
加好友78950405
QQ臨時會話
華強北商行笔记本,手機
淘宝阿里旺旺
沟通交流群:
水货thinkpad笔记本
工作时间:
11:00-22:00
电话:
18938079527
微信联系我们

QQ|手机版|华强北商行 ( 粤ICP备17062346号 )

JS of wanmeiff.com and vcpic.com Please keep this copyright information, respect of, thank you!JS of wanmeiff.com and vcpic.com Please keep this copyright information, respect of, thank you!

|网站地图 手机端 公司简介 联系方式 版权所有@

GMT+8, 2026-6-23 02:23 , Processed in 0.021933 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表