| 導航:起始頁 > Dive Into Python > HTTP Web 服務 > 處理壓縮數據 | << >> | ||||
深入 Python :Dive Into Python 中文版Python 從新手到專家 [Dip_5.4b_CPyUG_Release] |
|||||
你要支持的最后一個重要的 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" /> <-- 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,或許你沒想到,但是別為此擔心,因為那樣無法工作。
>>> 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 | |
全部放在一起 >> |