服务器发来压缩的响应,Scrapy 自己解压。支持哪些编码?叠加编码怎么处理?压缩体坏了、编码不认识会怎样?别人发一个 20 KB 的炸弹包要解压成 20 MB,能防住吗?这篇逐个实测。
服务器的编码 传输字节 回调 body 与原文相同 Content-Encoding 头(解压后) 响应类 / 错误 gzip 116 12026 是 (无) HtmlResponse x-gzip 116 12026 是 (无) HtmlResponse deflate(zlib 封装) 104 12026 是 (无) HtmlResponse deflate(裸 deflate) 98 12026 是 (无) HtmlResponse br 55 12026 是 (无) HtmlResponse zstd 74 12026 是 (无) HtmlResponse 叠加 gzip, br(先 gzip 再 br) 120 12026 是 (无) HtmlResponse 不支持的 compress 10 10 否 compress Response 声称 gzip 其实没压缩 12026 - - BadGzipFile: Not a gzipped file (b'<h') 截断的 gzip 60 28 否 (无) HtmlResponse 无压缩 12026 12026 是 (无) HtmlResponse 服务器看到的 Accept-Encoding:['gzip, deflate, br, zstd'] stats:httpcompression/response_bytes=84220, httpcompression/response_count=9 警告:[scrapy.downloadermiddlewares.httpcompression] WARNING: HttpCompressionMiddleware cannot decode the response for http://127.0.0.1:9960/x?k=%E4%B8%8D%E
请求里的 Accept-Encoding 写死 gzip, deflate, br, zstd。响应按 Content-Encoding 解压,叠加编码从最后一个往前解,裸 deflate 也能处理;解出的 body 替换原 body,Content-Encoding 头被删除,响应类重新判定。不认识的编码原样保留并警告;数据损坏会抛出对应的异常(如 BadGzipFile),由 errback 接住;被截断的流返回已解出的部分。
服务器发送 20,406 字节(gzip),解压后 20,971,520 字节
默认(MAXSIZE 1 GiB、WARNSIZE 32 MiB):callback 20971520;日志 [];stats {'httpcompression/response_bytes': 20971520, 'httpcompression/response_count': 1}
DOWNLOAD_WARNSIZE = 10 MiB:callback 20971520;日志 ['[scrapy.downloadermiddlewares.httpcompression] WARNING: <200 http://127.0.0.1:9960/x?k=BOMB> body size after decompression (20971520 B) is larger than'];stats {'httpcompression/response_bytes': 20971520, 'httpcompression/response_count': 1}
DOWNLOAD_MAXSIZE = 10 MiB:errback IgnoreRequest: Ignored response <200 http://127.0.0.1:9960/x?k=BOMB> because its body;日志 ['[scrapy.downloadermiddlewares.httpcompression] WARNING: Ignored response <200 http://127.0.0.1:9960/x?k=BOMB> because its body (20406 B compressed, 10'];stats {}解压按块进行,超过 DOWNLOAD_MAXSIZE 就放弃并丢弃响应;超过 DOWNLOAD_WARNSIZE 只记警告。默认上限 1 GiB、警告 32 MiB。
HEAD /x?k=gzip:回调 [{'n': 0, 'ce': 'gzip', 't': 139.4}]
COMPRESSION_ENABLED = 0:服务器看到的 Accept-Encoding = None;服务器仍发了 gzip,回调拿到 116 字节(开头 1f8b = gzip 魔数 1f8b),Content-Encoding = gzipHEAD 请求不解压;COMPRESSION_ENABLED = 0 时不发 Accept-Encoding 也不解压。