11.8. 处理压缩数据

你要支持的最后一个重要的 HTTP 特性是压缩。许多 web 服务具有发送压缩数据的能力,这可以将网络线路上传输的大量数据消减 60% 以上。这尤其适用于 XML web 服务,因为 XML 数据 的压缩率可以很高。

服务器不会为你发送压缩数据,除非你告诉服务器你可以处理压缩数据。

例 11.14. 告诉服务器你想获得压缩数据

>>> import urllib2, httplib
>>> httplib.HTTPConnection.debuglevel = 1
>>> request = urllib2.Request('http://www.bhlaab.com/887/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
这是关键:一创建了 Request 对象,就添加一个 Accept-encoding 头信息告诉服务器你能接受 gzip 压缩数据。gzip 是你使用的压缩算法的名称。理论上你可以使用其它的压缩算法,但是 gzip 是 web 服务器上使用率高达 99% 的一种。
这是你的头信息传越网络线路的过程。
这是服务器的返回信息:Content-Encoding: gzip 头信息意味着你要回得的数据已经被 gzip 压缩了。
Content-Length 头信息是已压缩数据的长度,并非解压缩数据的长度。一会儿你会看到,实际的解压缩数据长度为 15955,因此 gzip 压缩节省了 60% 以上的网络带宽!

例 11.15. 解压缩数据

>>> 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://www.bhlaab.com/428/atom/ns#"
  xmlns:dc="http://www.bhlaab.com/609/dc/elements/1.1/"
  xml:lang="en">
  <title mode="escaped">dive into mark</title>
  <link rel="alternate" type="text/html" href="http://www.bhlaab.com/626/"/>
  <-- rest of feed omitted for brevity -->
>>> len(data)
15955
继续上面的例子,f 是一个从 URL 开启器返回的类文件对象。使用它的 read() 方法将正常地获得非压缩数据,但是因为这个数据已经被 gzip 压缩过,所以这只是获得你想要的最终数据的第一步。
好吧,只是先得有点儿凌乱的步骤。Python 有一个 gzip 模块,它能读取 (当然也能写入) 磁盘上的 gzip 压缩文件。但是磁盘上还没有文件,只在内存里有一个 gzip 压缩缓冲区,并且你不想仅仅为了解压缩而写出一个临时文件。那么怎么做来从内存数据 (compresseddata) 创建类文件对象呢?这需要使用 StringIO 模块。你首次看到 StringIO 模块是在上一章,但现在你会发现它的另一种用法。
现在你可以创建 GzipFile 的一个实例,并且告诉它其中的 “文件” 是一个类文件对象 compressedstream
这是做所有工作的一行:从 GzipFile 中 “读取” 将会解压缩数据。感到奇妙吗?是的,它确实解压缩了数据。gzipper 是一个类文件对象,它代表一个 gzip 压缩文件。尽管这个 “文件” 并非一个磁盘上的真实文件;但 gzipper 还是从你用 StringIO 包装了压缩数据的类文件对象中 “读取” 数据,而它仅仅是内存中的变量 compresseddata。压缩的数据来自哪呢?最初你从远程 HTTP 服务器下载它,通过从用 urllib2.build_opener 创建的类文件对象中 “读取”。令人吃惊吧,这就是所有的步骤。链条上的每一步都完全不知道上一步在造假。
看看吧,实际的数据 (实际为 15955 bytes)。

等等!” 我听见你在叫。“还能更简单吗!” 我知道你在想什么。你在,既然 opener.open 返回一个类文件对象,那么为什么不抛弃中间件 StringIO 而通过 f 直接访问 GzipFile 呢?OK,时时彩计划软件公式:或许你没想到,但是别为此担心,因为那样无法工作。

例 11.16. 从服务器直接解压缩数据

>>> 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'
继续前面的例子,你已经有一个设置了 Accept-encoding: gzip 头信息的 Request 对象。
简单地打开请求将获得你的头信息 (虽然还没下载任何数据)。正如你从 Content-Encoding 头信息所看到的,这个数据被要求用 gzip 压缩发送。
opener.open 返回了一个类文件对象,从头信息中你可以获知,你将获得 gzip 压缩数据。为什么不简单地通过那个类文件对象直接访问 GzipFile 呢?当你从 GzipFile 实例 “读取” 时,它将从远程 HTTP 服务器 “读取” 被压缩的数据并且立即解压缩。这是个好主意,但是不行。由 gzip 压缩的工作方式所致,GzipFile 需要存储其位置并在压缩文件上往返游走。当 “文件” 是来自远程服务器的字节流时无法工作;你能用它做的所有工作就是一次返回一个字节流,而不是在字节流上往返。所以使用 StringIO 这种看上去不太优雅的手段是最好的解决方案:下载压缩的数据,用 StringIO 创建一个类文件对象,并从中解压缩数据。
老时时彩玩法技巧 重庆时时彩微信群 时时彩开奖报号软件 新疆时时彩数据 时时彩定位软件
重庆时时彩什么是跨度 重庆时时彩私彩平台 三分时时彩开奖走势图 时时彩综合走势有图 2013最新时时彩软件
谁有时时彩助赢软件 重庆时时彩有人玩吗 重庆彩店铺版平台出租 新疆时时彩助赢软件 时时彩早上几点开盘
江西时时彩现场开奖结果 爱博时时彩平台 江西时时彩规则 重庆时时彩注册账号 遂宁时时彩玩法