<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            11.8. 處理壓縮數據

            你要支持的最后一個重要的 HTTP 特性是壓縮。許多 web 服務具有發送壓縮數據的能力,這可以將網絡線路上傳輸的大量數據消減 60% 以上。這尤其適用于 XML web 服務,因為 XML 數據 的壓縮率可以很高。

            服務器不會為你發送壓縮數據,除非你告訴服務器你可以處理壓縮數據。

            例 11.14. 告訴服務器你想獲得壓縮數據

            >>> import urllib2, httplib
            >>> httplib.HTTPConnection.debuglevel = 1
            >>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml')
            >>> request.add_header('Accept-encoding', 'gzip')        1
            >>> 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                                    2
            '
            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                           3
            header: Content-Length: 6289                             4
            header: Connection: close
            header: Content-Type: application/atom+xml
            
            1 這是關鍵:一創建了 Request 對象,就添加一個 Accept-encoding 頭信息告訴服務器你能接受 gzip 壓縮數據。gzip 是你使用的壓縮算法的名稱。理論上你可以使用其它的壓縮算法,但是 gzip 是 web 服務器上使用率高達 99% 的一種。
            2 這是你的頭信息傳越網絡線路的過程。
            3 這是服務器的返回信息:Content-Encoding: gzip 頭信息意味著你要回得的數據已經被 gzip 壓縮了。
            4 Content-Length 頭信息是已壓縮數據的長度,并非解壓縮數據的長度。一會兒你會看到,實際的解壓縮數據長度為 15955,因此 gzip 壓縮節省了 60% 以上的網絡帶寬!

            例 11.15. 解壓縮數據

            >>> compresseddata = f.read()                              1
            >>> len(compresseddata)
            6289
            >>> import StringIO
            >>> compressedstream = StringIO.StringIO(compresseddata)   2
            >>> import gzip
            >>> gzipper = gzip.GzipFile(fileobj=compressedstream)      3
            >>> data = gzipper.read()                                  4
            >>> print data                                             5
            <?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
            
            1 繼續上面的例子,f 是一個從 URL 開啟器返回的類文件對象。使用它的 read() 方法將正常地獲得非壓縮數據,但是因為這個數據已經被 gzip 壓縮過,所以這只是獲得你想要的最終數據的第一步。
            2 好吧,只是先得有點兒凌亂的步驟。Python 有一個 gzip 模塊,它能讀取 (當然也能寫入) 磁盤上的 gzip 壓縮文件。但是磁盤上還沒有文件,只在內存里有一個 gzip 壓縮緩沖區,并且你不想僅僅為了解壓縮而寫出一個臨時文件。那么怎么做來從內存數據 (compresseddata) 創建類文件對象呢?這需要使用 StringIO 模塊。你首次看到 StringIO 模塊是在上一章,但現在你會發現它的另一種用法。
            3 現在你可以創建 GzipFile 的一個實例,并且告訴它其中的 “文件” 是一個類文件對象 compressedstream
            4 這是做所有工作的一行:從 GzipFile 中 “讀取” 將會解壓縮數據。感到奇妙嗎?是的,它確實解壓縮了數據。gzipper 是一個類文件對象,它代表一個 gzip 壓縮文件。盡管這個 “文件” 并非一個磁盤上的真實文件;但 gzipper 還是從你用 StringIO 包裝了壓縮數據的類文件對象中 “讀取” 數據,而它僅僅是內存中的變量 compresseddata。壓縮的數據來自哪呢?最初你從遠程 HTTP 服務器下載它,通過從用 urllib2.build_opener 創建的類文件對象中 “讀取”。令人吃驚吧,這就是所有的步驟。鏈條上的每一步都完全不知道上一步在造假。
            5 看看吧,實際的數據 (實際為 15955 bytes)。

            等等!” 我聽見你在叫。“還能更簡單嗎!” 我知道你在想什么。你在,既然 opener.open 返回一個類文件對象,那么為什么不拋棄中間件 StringIO 而通過 f 直接訪問 GzipFile 呢?OK,或許你沒想到,但是別為此擔心,因為那樣無法工作。

            例 11.16. 從服務器直接解壓縮數據

            >>> f = opener.open(request)                  1
            >>> f.headers.get('Content-Encoding')         2
            'gzip'
            >>> data = gzip.GzipFile(fileobj=f).read()    3
            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 繼續前面的例子,你已經有一個設置了 Accept-encoding: gzip 頭信息的 Request 對象。
            2 簡單地打開請求將獲得你的頭信息 (雖然還沒下載任何數據)。正如你從 Content-Encoding 頭信息所看到的,這個數據被要求用 gzip 壓縮發送。
            3 opener.open 返回了一個類文件對象,從頭信息中你可以獲知,你將獲得 gzip 壓縮數據。為什么不簡單地通過那個類文件對象直接訪問 GzipFile 呢?當你從 GzipFile 實例 “讀取” 時,它將從遠程 HTTP 服務器 “讀取” 被壓縮的數據并且立即解壓縮。這是個好主意,但是不行。由 gzip 壓縮的工作方式所致,GzipFile 需要存儲其位置并在壓縮文件上往返游走。當 “文件” 是來自遠程服務器的字節流時無法工作;你能用它做的所有工作就是一次返回一個字節流,而不是在字節流上往返。所以使用 StringIO 這種看上去不太優雅的手段是最好的解決方案:下載壓縮的數據,用 StringIO 創建一個類文件對象,并從中解壓縮數據。
            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线