
当系统中安装了 zstandard 库时,Requests 会自动在 Accept-Encoding 请求头中加入 zstd,导致服务器返回 zstd 压缩的二进制内容而未自动解压,从而破坏原有脚本逻辑;可通过卸载 zstandard 或全局禁用 zstd 支持来彻底解决。
当系统中安装了 `zstandard` 库时,requests 会自动在 `accept-encoding` 请求头中加入 `zstd`,导致服务器返回 zstd 压缩的二进制内容而未自动解压,从而破坏原有脚本逻辑;可通过卸载 `zstandard` 或全局禁用 zstd 支持来彻底解决。
在 Python 生态中,requests 库默认支持多种压缩编码(如 gzip、deflate、br),并会在发送请求时根据已安装的第三方库动态扩展 Accept-Encoding 头。自 requests v2.31.0 起(2023年中引入),若检测到系统已安装 zstandard(即 pip install zstandard),便会自动将 zstd 添加至默认的 Accept-Encoding: gzip, deflate, br, zstd 中——但关键在于:Requests 当前版本(截至 2026 年)仍不原生支持 zstd 解压缩。这意味着:
- 服务器若响应
Content-Encoding: zstd,Requests 不会自动解压; -
.content返回的是原始 zstd 压缩字节流(如你看到的乱码二进制),而非可读 HTML; -
.text或.json()等方法将直接失败或解析出错。
这正是你的 Synology DSM7 升级后脚本异常的根本原因:DSM7 的 Python 环境可能预装或间接依赖了 zstandard(例如通过 trafilatura、httpx 或其他爬虫工具链),触发了该行为。
✅ 推荐解决方案(按优先级排序)
1. 卸载 zstandard(最简单、最彻底)
pip uninstall zstandard -y
✅ 优势:一劳永逸,不影响任何其他库;Requests 检测不到该模块,自然不会添加
zstd到Accept-Encoding。
⚠️ 注意:仅当你不需要 zstd 压缩功能(如不使用zstandard进行本地数据压缩/解压)时适用。
2. 全局禁用 zstd(保留库但阻止 Requests 启用)
Requests 的编码协商逻辑位于 urllib3.util.response.acceptable_encodings(),但更实用的方式是在发起请求前显式覆盖默认头。可在项目入口统一设置:
import requests
# 全局禁用 zstd:重置 Session 的默认 Accept-Encoding
session = requests.Session()
session.headers['Accept-Encoding'] = 'gzip, deflate, br' # 显式排除 zstd
# 后续所有请求均生效
response = session.get("https://www.23andmedatasettlement.com/")
print(response.text[:200]) # 正常输出 HTML 片段
或者,若必须使用 requests.get 等顶层函数,可 monkey patch(谨慎使用):
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 强制所有 requests.* 方法默认不发 zstd
original_send = HTTPAdapter.send
def patched_send(self, request, **kwargs):
# 移除 Accept-Encoding 中的 zstd
ae = request.headers.get('Accept-Encoding', '')
if 'zstd' in ae:
request.headers['Accept-Encoding'] = ', '.join(
e.strip() for e in ae.split(',') if e.strip().lower() != 'zstd'
)
return original_send(self, request, **kwargs)
HTTPAdapter.send = patched_send
3. 临时绕过(单次请求)
requests.get(
"https://www.23andmedatasettlement.com/",
headers={"Accept-Encoding": "gzip, deflate, br"} # 显式指定,覆盖默认
)
? 验证是否已修复
执行以下代码确认 zstd 不再出现在请求头中:
import requests
resp = requests.get("https://httpbin.org/get", headers={"User-Agent": "test"})
print("Actual Accept-Encoding:", resp.request.headers.get("Accept-Encoding"))
# ✅ 正确输出应为:gzip, deflate, br (不含 zstd)
? 补充说明:为什么不能靠 verify=False 或 stream=True?
-
verify=False仅跳过 SSL 校验,与编码无关; -
stream=True仅延迟响应体读取,不改变解码行为; -
response.raw.read()仍返回原始 zstd 字节,需手动解压(需zstandard+ 额外代码),违背“零修改脚本”的初衷。
? 总结建议
| 方案 | 适用场景 | 维护成本 | 是否推荐 |
|---|---|---|---|
pip uninstall zstandard |
无 zstd 业务需求 | ★☆☆☆☆(一次) | ✅ 强烈推荐 |
Session 设置默认头 |
需保留 zstandard 作他用 |
★★☆☆☆ | ✅ 推荐 |
| Monkey patch | 大型遗留项目无法改初始化逻辑 | ★★★★☆ | ⚠️ 仅应急 |
✅ 最佳实践:在自动化部署脚本(如 Dockerfile 或 DSM 任务计划)中,明确声明
RUN pip uninstall -y zstandard,从源头规避风险。Python 3.14 已将compression.zstd纳入标准库,但 Requests 尚未适配其解压能力——因此现阶段主动禁用仍是生产环境的稳健选择。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











