这个gzip格式有什么问题?

3

我使用以下Python代码从启用gzip压缩的服务器下载网页:

url = "http://www.v-gn.de/wbb/"
import urllib2
request = urllib2.Request(url)
request.add_header('Accept-encoding', 'gzip')
response = urllib2.urlopen(request)
content = response.read()
response.close()

import gzip
from StringIO import StringIO
html = gzip.GzipFile(fileobj=StringIO(content)).read()

这个方法通常可行,但对于指定的URL会出现struct.error异常。如果我使用带有“Accept-encoding”标头的wget,也会得到类似的结果。然而,浏览器似乎能够解压缩响应。
因此我的问题是:是否有一种方法可以让我的Python代码在不禁用压缩(即删除“Accept-encoding”标头)的情况下解压HTTP响应?
为了完整起见,这是我用于wget的命令行:
wget --user-agent="Mozilla" --header="Accept-Encoding: gzip,deflate" http://www.v-gn.de/wbb/
3个回答

4

看起来你可以在 gzip.GzipFile 对象上调用 readline(),但是因为文件突然结束,read() 会引发一个 struct.error

既然 readline 能正常工作(除了最后),你可以这样做:

import urllib2
import StringIO
import gzip
import struct

url = "http://www.v-gn.de/wbb/"
request = urllib2.Request(url)
request.add_header('Accept-encoding', 'gzip')
response = urllib2.urlopen(request)
content = response.read()
response.close()
fh=StringIO.StringIO(content)
html = gzip.GzipFile(fileobj=StringIO.StringIO(content))
try:
    for line in html:
        line=line.rstrip()
        print(line)
except struct.error:
    pass

3
从urllib2.HTTPHandler派生并重写http_open()方法来创建您的处理程序。
import gzip
from StringIO import StringIO
import httplib, urllib, urllib2
class GzipHandler(urllib2.HTTPHandler):
    def http_open(self, req):
        req.add_header('Accept-encoding', 'gzip')
        r = self.do_open(httplib.HTTPConnection, req)
        if (
            'Content-Encoding'in r.headers and
            r.headers['Content-Encoding'] == 'gzip'
        ):
            fp = gzip.GzipFile(fileobj=StringIO(r.read()))
        else:
            fp = r
        response = urllib.addinfourl(fp, r.headers, r.url, r.code)
        response.msg = r.msg
        return respsone

然后构建您的打开器。
def retrieve(url):
    request = urllib2.Request(url)
    opener = urllib2.build_opener(GzipHandler)
    return opener.open(request)

这种方法的区别在于它检查服务器是否返回gzip响应,并且是在请求期间完成而非之后。
更多信息请参见:

这并没有真正回答原始问题,但确实是正确的做法。我仍然担心有些故障的服务器会返回“Content-encoding: gzip”头部,但实际上发送的数据是原始的。 - itsadok
你试过了吗? 对我有效。 我得到了一个长度为151860的响应,未经压缩。 这里发布的其他实现存在错误,我并没有仔细研究它们。 或许以后再说。 - Derrick Petzold
是的,原始问题中的网站似乎已经解决了他们的问题,所以我没有好的测试方法。在任何情况下,使用readline()而不是read()可以解决该问题,在retrieve()之外完成。 - itsadok

3
我运行了你指定的命令。它下载了一个gzip压缩的数据到index.html中。我将index.html重命名为index.html.gz。我尝试运行gzip -d inedx.html.gz,但出现了错误:gzip: index.html.gz: unexpected end of file
第二次尝试是zcat index.html.gz,这个方法成功了,只是在</html>标签后面打印了与上述相同的错误。
$ zcat index.html.gz
...
  </td>
 </tr>
</table>


</body>
</html>
gzip: index.html.gz: unexpected end of file

服务器出现故障。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接