您当前位置:首页 > 深入浅出 Python > HTTP Web 服务 > 处理压缩数据 | << >> | ||||
深入浅出 Python从 Python 新手到专家 |
您想要支持的最后一个重要的 HTTP 功能是压缩。许多 Web 服务能够发送压缩数据,这可以将通过网络发送的数据量减少 60% 或更多。这对于 XML Web 服务尤其如此,因为 XML 数据的压缩率很高。
除非您告诉服务器您可以处理压缩数据,否则服务器不会向您发送压缩数据。
>>> import urllib2, httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml') >>> request.add_header('Accept-encoding', 'gzip')>>> opener = urllib2.build_opener() >>> f = opener.open(request) connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 Accept-encoding: gzip
' reply: 'HTTP/1.1 200 OK\r\n' header: Date: Thu, 15 Apr 2004 22:24:39 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Last-Modified: Thu, 15 Apr 2004 19:45:21 GMT header: ETag: "e842a-3e53-55d97640" header: Accept-Ranges: bytes header: Vary: Accept-Encoding header: Content-Encoding: gzip
header: Content-Length: 6289
header: Connection: close header: Content-Type: application/atom+xml
>>> compresseddata = f.read()>>> len(compresseddata) 6289 >>> import StringIO >>> compressedstream = StringIO.StringIO(compresseddata)
>>> import gzip >>> gzipper = gzip.GzipFile(fileobj=compressedstream)
>>> data = gzipper.read()
>>> print data
<?xml version="1.0" encoding="iso-8859-1"?> <feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en"> <title mode="escaped">dive into mark</title> <link rel="alternate" type="text/html" href="http://diveintomark.org/"/> <-- rest of feed omitted for brevity --> >>> len(data) 15955
![]() |
继续前面的示例,f 是从 URL 打开器返回的类文件对象。使用其 read() 方法通常会获取未压缩的数据,但由于此数据已使用 gzip 压缩,因此这只是获取您真正想要的数据的第一步。 |
![]() |
好的,这一步需要一些繁琐的变通方法。Python 有一个 gzip 模块,可以读取(实际上是写入)磁盘上的 gzip 压缩文件。但您在磁盘上没有文件,而是在内存中有一个 gzip 压缩缓冲区,并且您不想仅仅为了解压缩它而写出一个临时文件。因此,您要做的是使用 StringIO 模块从内存中的数据 (compresseddata) 创建一个类文件对象。您在 上一章 中第一次看到了 StringIO 模块,但现在您找到了它的另一个用途。 |
![]() |
现在您可以创建一个 GzipFile 的实例,并告诉它它的“文件”是类文件对象 compressedstream。 |
![]() |
这是完成所有实际工作的代码行:从 GzipFile“读取”将解压缩数据。奇怪吗?是的,但这在某种程度上是说得通的。gzipper 是一个类文件对象,它表示一个 gzip 压缩文件。但是,该“文件”不是磁盘上的真实文件;gzipper 实际上只是从您使用 StringIO 创建的类文件对象中“读取”,以包装压缩数据,而这些数据只在变量 compresseddata 的内存中。那么这些压缩数据是从哪里来的呢?您最初是通过从使用 urllib2.build_opener 构建的类文件对象中“读取”数据,从远程 HTTP 服务器下载的。令人惊讶的是,这一切都正常工作。链中的每一步都不知道前一步是伪造的。 |
![]() |
看,是真实的数据。(实际上是 15955 字节。) |
“等等!”我听到你哭了。“这可以更简单!”我知道你在想什么。您在想 opener.open 返回一个类文件对象,那么为什么不删除 StringIO 中间人,直接将 f 传递给 GzipFile 呢?好吧,也许您没有这么想,但不用担心,因为它不起作用。
>>> f = opener.open(request)>>> f.headers.get('Content-Encoding')
'gzip' >>> data = gzip.GzipFile(fileobj=f).read()
Traceback (most recent call last): File "<stdin>", line 1, in ? File "c:\python23\lib\gzip.py", line 217, in read self._read(readsize) File "c:\python23\lib\gzip.py", line 252, in _read pos = self.fileobj.tell() # Save current position AttributeError: addinfourl instance has no attribute 'tell'
<< 处理重定向 |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
整合所有内容 >> |