Python3 urlopen 读取异常 (gzip)

Python3 urlopen read weirdness (gzip)

我从 Schema.org 收到 URL。这是内容类型="text/html"

有时,read() 会按预期运行 b'< !DOCTYPE html> ....'

有时,read() returns something else b'\x1f\x8b\x08\x00\x00\x00\x00 ...'

try:
    with urlopen("http://schema.org/docs/releases.html") as f:
        txt = f.read()
except URLError:
    return

我尝试用 txt = f.read().decode("utf-8").encode() 解决这个问题,但这会导致错误...有时:UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8b in position 1: invalid start byte

明显的解决方法是测试第一个字节是否为十六进制并相应地处理它。

我的问题是:这是错误还是其他原因?

编辑 相关 question。显然,有时我会收到 gzipped 流。

最后 我通过将以下代码添加为 proposed here

解决了这个问题
if 31 == txt[0]:
    txt = decompress(txt, 16+MAX_WBITS)

问题依旧;为什么这个 return text/html 有时会压缩而其他时候压缩?

您确实收到了 gzip 压缩的响应。您应该可以通过以下方式避免它:

from urllib import request
try:
    req = request.Request("http://schema.org/docs/releases.html")
    req.add_header('Accept-Encoding', 'identity;q=1')
    with request.urlopen(req) as f:
        txt = f.read()
except request.URLError:
    return

此类别中还有其他问题,但我找不到解决问题实际原因的答案。

Python 的 urllib2.urlopen() 无法透明地处理压缩。它还默认不设置 Accept-Encoding 请求 header。此外,根据 HTTP 标准对这种情况的解释在过去已经发生了变化。

根据RFC2616

If no Accept-Encoding field is present in a request, the server MAY assume that the client will accept any content coding. In this case, if "identity" is one of the available content-codings, then the server SHOULD use the "identity" content-coding, unless it has additional information that a different content-coding is meaningful to the client.

不幸的是(至于用例),RFC7231 将其更改为

If no Accept-Encoding field is in the request, any content-coding is considered acceptable by the user agent.

意思是,当使用 urlopen() 执行请求时,您可以获得服务器决定使用的任何编码的响应,并且响应将是一致的。

schema.org 似乎由 google 托管,即它很可能位于分布式前端负载平衡器网络之后。因此,您得到的不同答案可能是从配置略有不同的负载均衡器返回的。

Google 工程师过去 advocated for the use HTTP compression,所以这也可能是一个有意识的决定。

所以作为一个教训:在使用urlopen()时我们需要设置Accept-Encoding.