是的,requests库默认自动解压gzip响应,只要Accept-Encoding未被覆盖或禁用,response.text/content即为解压后内容;urllib则需手动解压。

requests 默认是否自动解压 gzip?
是的,requests 库默认会自动处理 Content-Encoding: gzip 响应,只要服务器返回了正确的头部且数据格式合法,你拿到的 response.text 或 response.content 就已经是解压后的内容。
但这个“自动”有前提:你没手动设置 headers 覆盖掉 Accept-Encoding,也没显式禁用解压。
- 如果你在请求头里写了
headers={'Accept-Encoding': 'identity'},requests就不会要求 gzip,自然也不会解压 - 如果写了
headers={'Accept-Encoding': 'gzip'}却没配stream=True,一般不影响,但某些异常响应(比如空 body + gzip header)可能触发urllib3的解压异常 -
response.raw.read()返回的是原始压缩字节,不经过解压——这点容易被忽略
手动解压 gzip 响应体的典型场景
常见于你用了 stream=True、或绕过 requests 直接用 urllib.request / http.client,或者收到的响应头缺失 Content-Encoding 但实际 body 是 gzip 压缩的(有些反爬站点会故意这么干)。
这时得自己调 gzip.decompress():
立即学习“Python免费学习笔记(深入)”;
import gzip
import requests
<p>r = requests.get(url, stream=True)
compressed_data = r.raw.read() # 注意:不是 r.content
try:
html = gzip.decompress(compressed_data).decode('utf-8')
except (gzip.BadGzipFile, UnicodeDecodeError):</p><h1>可能不是 gzip,或编码不对</h1><pre class='brush:python;toolbar:false;'>pass</pre>-
gzip.decompress()要求输入是完整的 gzip 流,不能是分块传输中截断的一段 - 如果响应用了
deflate编码(少见但存在),gzip.decompress()会失败,得用zlib.decompress(data, -zlib.MAX_WBITS) - 别对
r.content再次解压——它已经被requests处理过了,重复解压会报BadGzipFile
requests 不解压的几种错误表现
最典型的是拿到一堆乱码或二进制内容,比如开头是 b'\x1f\x8b\x08'(gzip magic bytes),但 response.text 显示乱码、len(response.text) 异常小,或解析 HTML 时 lxml 报 ParserError: Document is empty。
- 检查
r.headers.get('Content-Encoding')是否为'gzip'或'deflate' - 打印
r.content[:10]看是否以b'\x1f\x8b'开头——是的话说明 raw body 是 gzip,但没被解压 - 确认没传
stream=True且又用了r.raw;或者传了stream=True却忘了读取前检查编码 - 某些代理或中间网关会删掉
Content-Encoding头但保留压缩 body,这时必须手动解压
用 urllib.request 处理 gzip 的注意事项
urllib.request 默认不自动解压 gzip,哪怕响应头里有 Content-Encoding: gzip,它也原样返回压缩字节。
你得自己判断并解压:
import urllib.request
import gzip
<p>req = urllib.request.Request(url)
with urllib.request.urlopen(req) as res:
raw = res.read()
encoding = res.headers.get('Content-Encoding', '')
if encoding == 'gzip':
html = gzip.decompress(raw).decode('utf-8')
else:
html = raw.decode('utf-8')-
urllib不像requests那样内置解压逻辑,也不支持Accept-Encoding自动协商 - 如果目标站强制 gzip 且没发
Content-Encoding头,你只能靠 magic bytes 判断:raw.startswith(b'\x1f\x8b') - 注意
urllib的res.read()会一次性加载全部响应体到内存,大文件慎用
真正麻烦的不是解压本身,而是判断“该不该解”以及“用哪种方式解”——gzip、deflate、br(Brotli)混用时,光靠 header 不可靠,得结合 magic bytes 和 fallback 尝试。


















