《深入Python》-11. HTTP Web 服务
《深入Python》-11. HTTP Web 服務(wù)
11. HTTP Web 服務(wù)
出處: http://www.woodpecker.org.cn/diveintopython/http_web_services/index.html
第?11?章?HTTP Web 服務(wù)
- 11.1. 概覽
- 11.2. 避免通過 HTTP 重復(fù)地獲取數(shù)據(jù)
- 11.3. HTTP 的特性
- 11.3.1. 用戶代理 (User-Agent)
- 11.3.2. 重定向 (Redirects)
- 11.3.3. Last-Modified/If-Modified-Since
- 11.3.4. ETag/If-None-Match
- 11.3.5. 壓縮 (Compression)
- 11.4. 調(diào)試 HTTP web 服務(wù)
- 11.5. 設(shè)置 User-Agent
- 11.6. 處理 Last-Modified 和 ETag
- 11.7. 處理重定向
- 11.8. 處理壓縮數(shù)據(jù)
- 11.9. 全部放在一起
- 11.10. 小結(jié)
11.1.?概覽
在講解如何下載 web 頁和如何從 URL 解析 XML時,你已經(jīng)學(xué)習(xí)了關(guān)于 HTML 處理和 XML 處理,接下來讓我們來更全面地探討有關(guān) HTTP web 服務(wù)的主題。
簡單地講,HTTP web 服務(wù)是指以編程的方式直接使用 HTTP 操作從遠(yuǎn)程服務(wù)器發(fā)送和接收數(shù)據(jù)。如果你要從服務(wù)器獲取數(shù)據(jù),直接使用 HTTP GET;如果您想發(fā)送新數(shù)據(jù)到服務(wù)器,使用 HTTP POST。(一些較高級的 HTTP web 服務(wù) API 也定義了使用 HTTP PUT 和 HTTP DELETE 修改和刪除現(xiàn)有數(shù)據(jù)的方法。) 換句話說,構(gòu)建在 HTTP 協(xié)議中的 “verbs (動作)” (GET, POST, PUT 和 DELETE) 直接映射為接收、發(fā)送、修改和刪除等應(yīng)用級別的操作。
這種方法的主要優(yōu)點(diǎn)是簡單,并且許多不同的站點(diǎn)充分印證了這樣的簡單性是受歡迎的。數(shù)據(jù) (通常是 XML 數(shù)據(jù)) 能靜態(tài)創(chuàng)建和存儲,或通過服務(wù)器端腳本和所有主流計算機(jī)語言 (包括用于下載數(shù)據(jù)的 HTTP 庫) 動態(tài)生成。調(diào)試也很簡單,因?yàn)槟梢栽谌我鉃g覽器中調(diào)用網(wǎng)絡(luò)服務(wù)來查看這些原始數(shù)據(jù)。現(xiàn)代瀏覽器甚至可以為您進(jìn)行良好的格式化并漂亮地打印這些 XML 數(shù)據(jù),以便讓您快速地瀏覽。
HTTP web 服務(wù)上的純 XML 應(yīng)用舉例:
- Amazon API 允許您從 Amazon.com 在線商店獲取產(chǎn)品信息。
- National Weather Service (美國) 和 Hong Kong Observatory (香港) 通過 web 服務(wù)提供天氣警報。
- Atom API 用來管理基于 web 的內(nèi)容。
- Syndicated feeds 應(yīng)用于 weblogs 和新聞?wù)军c(diǎn)中帶給您來自眾多站點(diǎn)的最新消息。
在后面的幾章里,我們將探索使用 HTTP 進(jìn)行數(shù)據(jù)發(fā)送和接收傳輸?shù)?API,但是不會將應(yīng)用語義映射到潛在的 HTTP 語義。(所有這些都是通過 HTTP POST 這個管道完成的。) 但是本章將關(guān)注使用 HTTP GET 從遠(yuǎn)程服務(wù)器獲取數(shù)據(jù),并且將探索幾個由純 HTTP web 服務(wù)帶來最大利益的 HTTP 特性。
如下所示為上一章曾經(jīng)看到過的 openanything 模塊的更高級版本:
例?11.1.?openanything.py
如果您還沒有下載本書附帶的樣例程序, 可以 下載本程序和其他樣例程序。
import urllib2, urlparse, gzip from StringIO import StringIOUSER_AGENT = 'OpenAnything/1.0 +http://diveintopython.org/http_web_services/'class SmartRedirectHandler(urllib2.HTTPRedirectHandler): def http_error_301(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers) result.status = code return result def http_error_302(self, req, fp, code, msg, headers): result = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers) result.status = code return result class DefaultErrorHandler(urllib2.HTTPDefaultErrorHandler): def http_error_default(self, req, fp, code, msg, headers):result = urllib2.HTTPError( req.get_full_url(), code, msg, headers, fp) result.status = code return result def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):'''URL, filename, or string --> streamThis function lets you define parsers that take any input source(URL, pathname to local or network file, or actual data as a string)and deal with it in a uniform manner. Returned object is guaranteedto have all the basic stdio read methods (read, readline, readlines).Just .close() the object when you're done with it.If the etag argument is supplied, it will be used as the value of anIf-None-Match request header.If the lastmodified argument is supplied, it must be a formatteddate/time string in GMT (as returned in the Last-Modified header ofa previous request). The formatted date/time will be usedas the value of an If-Modified-Since request header.If the agent argument is supplied, it will be used as the value of aUser-Agent request header.'''if hasattr(source, 'read'):return sourceif source == '-':return sys.stdinif urlparse.urlparse(source)[0] == 'http': # open URL with urllib2 request = urllib2.Request(source) request.add_header('User-Agent', agent) if etag: request.add_header('If-None-Match', etag) if lastmodified: request.add_header('If-Modified-Since', lastmodified) request.add_header('Accept-encoding', 'gzip') opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler())return opener.open(request) # try to open with native open function (if source is a filename)try:return open(source)except (IOError, OSError):pass# treat source as stringreturn StringIO(str(source))def fetch(source, etag=None, last_modified=None, agent=USER_AGENT): '''Fetch data and metadata from a URL, file, stream, or string'''result = {} f = openAnything(source, etag, last_modified, agent) result['data'] = f.read() if hasattr(f, 'headers'): # save ETag, if the server sent one result['etag'] = f.headers.get('ETag') # save Last-Modified header, if the server sent one result['lastmodified'] = f.headers.get('Last-Modified') if f.headers.get('content-encoding', '') == 'gzip': # data came back gzip-compressed, decompress it result['data'] = gzip.GzipFile(fileobj=StringIO(result['data']])).read()if hasattr(f, 'url'): result['url'] = f.url result['status'] = 200 if hasattr(f, 'status'): result['status'] = f.status f.close() return result進(jìn)一步閱讀
- Paul Prescod 認(rèn)為純 HTTP web 服務(wù)是 Internet 的未來。
11.2.?避免通過 HTTP 重復(fù)地獲取數(shù)據(jù)
假如說你想用 HTTP 下載資源,例如一個 Atom feed 匯聚。你不僅僅想下載一次;而是想一次又一次地下載它,如每小時一次,從提供 news feed 的站點(diǎn)獲得最新的消息。讓我們首先用一種直接而原始的方法來實(shí)現(xiàn)它,然后看看如何改進(jìn)它。
例?11.2.?用直接而原始的方法下載 feed
>>> import urllib >>> data = urllib.urlopen('http://diveintomark.org/xml/atom.xml').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 -->11.3.?HTTP 的特性
- 11.3.1. 用戶代理 (User-Agent)
- 11.3.2. 重定向 (Redirects)
- 11.3.3. Last-Modified/If-Modified-Since
- 11.3.4. ETag/If-None-Match
- 11.3.5. 壓縮 (Compression)
這里有五個你必須關(guān)注的 HTTP 重要特性。
11.3.1.?用戶代理 (User-Agent)
User-Agent 是一種客戶端告知服務(wù)器誰在什么時候通過 HTTP 請求了一個 web 頁、feed 匯聚或其他類型的 web 服務(wù)的簡單途徑。當(dāng)客戶端請求一個資源時,應(yīng)該盡可能明確發(fā)起請求的是誰,以便當(dāng)產(chǎn)生異常錯誤時,允許服務(wù)器端的管理員與客戶端的開發(fā)者取得聯(lián)系。
默認(rèn)情況下 Python 發(fā)送一個通用的 User-Agent:Python-urllib/1.15。下一節(jié),您將看到更加有針對性的 User-Agent。
11.3.2.?重定向 (Redirects)
有時資源移來移去。Web 站點(diǎn)重組內(nèi)容,頁面移動到了新的地址。甚至是 web 服務(wù)重組。原來位于 http://example.com/index.xml 的 feed 匯聚可能被移動到 http://example.com/xml/atom.xml。或者因?yàn)橐粋€機(jī)構(gòu)的擴(kuò)展或重組,整個域被遷移。例如,http://www.example.com/index.xml 可能被重定向到 http://server-farm-1.example.com/index.xml。
您每次從 HTTP 服務(wù)器請求任何類型的資源時,服務(wù)器的響應(yīng)中均包含一個狀態(tài)代碼。狀態(tài)代碼 200 的意思是 “一切正常,這就是您請求的頁面”。狀態(tài)代碼 404 的意思是 “頁面沒找到”。 (當(dāng)瀏覽 web 時,你可能看到過 404 errors。)
HTTP 有兩種不同的方法表示資源已經(jīng)被移動。狀態(tài)代碼 302 表示臨時重定向;這意味著 “哎呀,訪問內(nèi)容被臨時移動” (然后在 Location: 頭信息中給出臨時地址)。狀態(tài)代碼 301 表示永久重定向;這意味著 “哎呀,訪問內(nèi)容被永久移動” (然后在 Location: 頭信息中給出新地址)。如果您獲得了一個 302 狀態(tài)代碼和一個新地址,HTTP 規(guī)范說您應(yīng)該使用新地址獲取您的請求,但是下次您要訪問同一資源時,應(yīng)該使用原地址重試。但是如果您獲得了一個 301 狀態(tài)代碼和一個新地址,您應(yīng)該從此使用新地址。
當(dāng)從 HTTP 服務(wù)器接受到一個適當(dāng)?shù)臓顟B(tài)代碼時,urllib.urlopen 將自動 “跟蹤” 重定向,但不幸的是,當(dāng)它做了重定向時不會告訴你。 你將最終獲得所請求的數(shù)據(jù),卻絲毫不會察覺到在這個過程中一個潛在的庫 “幫助” 你做了一次重定向操作。因此你將繼續(xù)不斷地使用舊地址,并且每次都將獲得被重定向的新地址。這一過程要往返兩次而不是一次:太沒效率了!本章的后面,您將看到如何改進(jìn)這一點(diǎn),從而適當(dāng)?shù)厍矣行实靥幚碛谰弥囟ㄏ颉?/p>
11.3.3.?Last-Modified/If-Modified-Since
有些數(shù)據(jù)隨時都在變化。CNN.com 的主頁經(jīng)常幾分鐘就更新。另一方面,Google.com 的主頁幾個星期才更新一次 (當(dāng)他們上傳特殊的假日 logo,或?yàn)橐粋€新服務(wù)作廣告時)。 Web 服務(wù)是不變的:通常服務(wù)器知道你所請求的數(shù)據(jù)的最后修改時間,并且 HTTP 為服務(wù)器提供了一種將最近修改數(shù)據(jù)連同你請求的數(shù)據(jù)一同發(fā)送的方法。
如果你第二次 (或第三次,或第四次) 請求相同的數(shù)據(jù),你可以告訴服務(wù)器你上一次獲得的最后修改日期:在你的請求中發(fā)送一個 If-Modified-Since 頭信息,它包含了上一次從服務(wù)器連同數(shù)據(jù)所獲得的日期。如果數(shù)據(jù)從那時起沒有改變,服務(wù)器將返回一個特殊的 HTTP 狀態(tài)代碼 304,這意味著 “從上一次請求后這個數(shù)據(jù)沒有改變”。這一點(diǎn)有何進(jìn)步呢?當(dāng)服務(wù)器發(fā)送狀態(tài)編碼 304 時,不再重新發(fā)送數(shù)據(jù)。您僅僅獲得了這個狀態(tài)代碼。所以當(dāng)數(shù)據(jù)沒有更新時,你不需要一次又一次地下載相同的數(shù)據(jù);服務(wù)器假定你有本地的緩存數(shù)據(jù)。
所有現(xiàn)代的瀏覽器都支持最近修改 (last-modified) 的數(shù)據(jù)檢查。如果你曾經(jīng)訪問過某頁,一天后重新訪問相同的頁時發(fā)現(xiàn)它沒有變化,并奇怪第二次訪問時頁面加載得如此之快——這就是原因所在。你的瀏覽器首次 訪問時會在本地緩存頁面內(nèi)容,當(dāng)你第二次訪問,瀏覽器自動發(fā)送首次訪問時從服務(wù)器獲得的最近修改日期。服務(wù)器簡單地返回 304: Not Modified (沒有修改),因此瀏覽器就會知道從本地緩存加載頁面。在這一點(diǎn)上,Web 服務(wù)也如此智能。
Python 的 URL 庫沒有提供內(nèi)置的最近修改數(shù)據(jù)檢查支持,但是你可以為每一個請求添加任意的頭信息并在每一個響應(yīng)中讀取任意頭信息,從而自己添加這種支持。
11.3.4.?ETag/If-None-Match
ETag 是實(shí)現(xiàn)與最近修改數(shù)據(jù)檢查同樣的功能的另一種方法:沒有變化時不重新下載數(shù)據(jù)。其工作方式是:服務(wù)器發(fā)送你所請求的數(shù)據(jù)的同時,發(fā)送某種數(shù)據(jù)的 hash (在 ETag 頭信息中給出)。hash 的確定完全取決于服務(wù)器。當(dāng)?shù)诙握埱笙嗤臄?shù)據(jù)時,你需要在 If-None-Match: 頭信息中包含 ETag hash,如果數(shù)據(jù)沒有改變,服務(wù)器將返回 304 狀態(tài)代碼。與最近修改數(shù)據(jù)檢查相同,服務(wù)器僅僅 發(fā)送 304 狀態(tài)代碼;第二次將不為你發(fā)送相同的數(shù)據(jù)。在第二次請求時,通過包含 ETag hash,你告訴服務(wù)器:如果 hash 仍舊匹配就沒有必要重新發(fā)送相同的數(shù)據(jù),因?yàn)槟氵€有上一次訪問過的數(shù)據(jù)。
Python 的 URL 庫沒有對 ETag 的內(nèi)置支持,但是在本章后面你將看到如何添加這種支持。
11.3.5.?壓縮 (Compression)
最后一個重要的 HTTP 特性是 gzip 壓縮。 關(guān)于 HTTP web 服務(wù)的主題幾乎總是會涉及在網(wǎng)絡(luò)線路上傳輸?shù)?XML。XML 是文本,而且還是相當(dāng)冗長的文本,而文本通常可以被很好地壓縮。當(dāng)你通過 HTTP 請求一個資源時,可以告訴服務(wù)器,如果它有任何新數(shù)據(jù)要發(fā)送給我時,請以壓縮的格式發(fā)送。在你的請求中包含 Accept-encoding: gzip 頭信息,如果服務(wù)器支持壓縮,它將返回由 gzip 壓縮的數(shù)據(jù)并且使用 Content-encoding: gzip 頭信息標(biāo)記。
Python 的 URL 庫本身沒有內(nèi)置對 gzip 壓縮的支持,但是你能為請求添加任意的頭信息。Python 還提供了一個獨(dú)立的 gzip 模塊,它提供了對數(shù)據(jù)進(jìn)行解壓縮的功能。
注意我們用于下載 feed 匯聚的小單行腳本并不支持任何這些 HTTP 特性。讓我們來看看如何改善它。
11.4.?調(diào)試 HTTP web 服務(wù)
首先,讓我們開啟 Python HTTP 庫的調(diào)試特性并查看網(wǎng)絡(luò)線路上的傳輸過程。這對本章的全部內(nèi)容都很有用,因?yàn)槟銓⑻砑釉絹碓蕉嗟奶匦浴?/p>
例?11.3.?調(diào)試 HTTP
>>> import httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> import urllib >>> feeddata = urllib.urlopen('http://diveintomark.org/xml/atom.xml').read() connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/1.15 ' reply: 'HTTP/1.1 200 OKrn' header: Date: Wed, 14 Apr 2004 22:27:30 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Content-Type: application/atom+xml header: Last-Modified: Wed, 14 Apr 2004 22:14:38 GMT header: ETag: "e8284-68e0-4de30f80" header: Accept-Ranges: bytes header: Content-Length: 26848 header: Connection: close11.5.?設(shè)置 User-Agent
改善你的 HTTP web 服務(wù)客戶端的第一步就是用 User-Agent 適當(dāng)?shù)罔b別你自己。為了做到這一點(diǎn),你需要遠(yuǎn)離基本的 urllib 而深入到 urllib2。
例?11.4.?urllib2 介紹
>>> import httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> import urllib2 >>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml') >>> opener = urllib2.build_opener() >>> feeddata = opener.open(request).read() connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 200 OKrn' header: Date: Wed, 14 Apr 2004 23:23:12 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Content-Type: application/atom+xml header: Last-Modified: Wed, 14 Apr 2004 22:14:38 GMT header: ETag: "e8284-68e0-4de30f80" header: Accept-Ranges: bytes header: Content-Length: 26848 header: Connection: close11.6.?處理 Last-Modified 和 ETag
既然你知道如何在你的 web 服務(wù)請求中添加自定義的 HTTP 頭信息,接下來看看如何添加 Last-Modified 和 ETag 頭信息的支持。
下面的這些例子將以調(diào)試標(biāo)記置為關(guān)閉的狀態(tài)來顯示輸出結(jié)果。如果你還停留在上一部分的開啟狀態(tài),可以使用 httplib.HTTPConnection.debuglevel = 0 將其設(shè)置為關(guān)閉狀態(tài)。或者,如果你認(rèn)為有幫助也可以保持為開啟狀態(tài)。
例?11.6.?測試 Last-Modified
>>> import urllib2 >>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml') >>> opener = urllib2.build_opener() >>> firstdatastream = opener.open(request) >>> firstdatastream.headers.dict {'date': 'Thu, 15 Apr 2004 20:42:41 GMT', 'server': 'Apache/2.0.49 (Debian GNU/Linux)', 'content-type': 'application/atom+xml','last-modified': 'Thu, 15 Apr 2004 19:45:21 GMT', 'etag': '"e842a-3e53-55d97640"','content-length': '15955', 'accept-ranges': 'bytes', 'connection': 'close'} >>> request.add_header('If-Modified-Since', ... firstdatastream.headers.get('Last-Modified')) >>> seconddatastream = opener.open(request) Traceback (most recent call last):File "<stdin>", line 1, in ?File "c:python23liburllib2.py", line 326, in open'_open', req)File "c:python23liburllib2.py", line 306, in _call_chainresult = func(*args)File "c:python23liburllib2.py", line 901, in http_openreturn self.do_open(httplib.HTTP, req)File "c:python23liburllib2.py", line 895, in do_openreturn self.parent.error('http', req, fp, code, msg, hdrs)File "c:python23liburllib2.py", line 352, in errorreturn self._call_chain(*args)File "c:python23liburllib2.py", line 306, in _call_chainresult = func(*args)File "c:python23liburllib2.py", line 412, in http_error_defaultraise HTTPError(req.get_full_url(), code, msg, hdrs, fp) urllib2.HTTPError: HTTP Error 304: Not Modified11.7.?處理重定向
你可以使用兩種不同的自定義 URL 處理器來處理永久重定向和臨時重定向。
首先,讓我們來看看重定向處理的必要性。
例?11.10.?沒有重定向處理的情況下,訪問 web 服務(wù)
>>> import urllib2, httplib >>> httplib.HTTPConnection.debuglevel = 1 >>> request = urllib2.Request( ... 'http://diveintomark.org/redir/example301.xml') >>> opener = urllib2.build_opener() >>> f = opener.open(request) connect: (diveintomark.org, 80) send: ' GET /redir/example301.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 301 Moved Permanentlyrn' header: Date: Thu, 15 Apr 2004 22:06:25 GMT header: Server: Apache/2.0.49 (Debian GNU/Linux) header: Location: http://diveintomark.org/xml/atom.xml header: Content-Length: 338 header: Connection: close header: Content-Type: text/html; charset=iso-8859-1 connect: (diveintomark.org, 80) send: ' GET /xml/atom.xml HTTP/1.0 Host: diveintomark.org User-agent: Python-urllib/2.1 ' reply: 'HTTP/1.1 200 OKrn' header: Date: Thu, 15 Apr 2004 22:06:25 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: Content-Length: 15955 header: Connection: close header: Content-Type: application/atom+xml >>> f.url 'http://diveintomark.org/xml/atom.xml' >>> f.headers.dict {'content-length': '15955', 'accept-ranges': 'bytes', 'server': 'Apache/2.0.49 (Debian GNU/Linux)', 'last-modified': 'Thu, 15 Apr 2004 19:45:21 GMT', 'connection': 'close', 'etag': '"e842a-3e53-55d97640"', 'date': 'Thu, 15 Apr 2004 22:06:25 GMT', 'content-type': 'application/atom+xml'} >>> f.status Traceback (most recent call last):File "<stdin>", line 1, in ? AttributeError: addinfourl instance has no attribute 'status'11.8.?處理壓縮數(shù)據(jù)
你要支持的最后一個重要的 HTTP 特性是壓縮。許多 web 服務(wù)具有發(fā)送壓縮數(shù)據(jù)的能力,這可以將網(wǎng)絡(luò)線路上傳輸?shù)拇罅繑?shù)據(jù)消減 60% 以上。這尤其適用于 XML web 服務(wù),因?yàn)?XML 數(shù)據(jù) 的壓縮率可以很高。
服務(wù)器不會為你發(fā)送壓縮數(shù)據(jù),除非你告訴服務(wù)器你可以處理壓縮數(shù)據(jù)。
例?11.14.?告訴服務(wù)器你想獲得壓縮數(shù)據(jù)
>>> 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 OKrn' 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+xml11.9.?全部放在一起
你已經(jīng)看到了構(gòu)造一個智能的 HTTP web 客戶端的所有片斷。現(xiàn)在讓我們看看如何將它們整合到一起。
例?11.17.?openanything 函數(shù)
這個函數(shù)定義在 openanything.py 中。
def openAnything(source, etag=None, lastmodified=None, agent=USER_AGENT):# non-HTTP code omitted for brevityif urlparse.urlparse(source)[0] == 'http': # open URL with urllib2 request = urllib2.Request(source) request.add_header('User-Agent', agent) if etag: request.add_header('If-None-Match', etag) if lastmodified: request.add_header('If-Modified-Since', lastmodified) request.add_header('Accept-encoding', 'gzip') opener = urllib2.build_opener(SmartRedirectHandler(), DefaultErrorHandler()) return opener.open(request)11.10.?小結(jié)
openanything.py 及其函數(shù)現(xiàn)在可以完美地工作了。
每個客戶端都應(yīng)該支持 HTTP web 服務(wù)的以下 5 個重要特性:
- 通過設(shè)置適當(dāng)?shù)?User-Agent 識別你的應(yīng)用。
- 適當(dāng)?shù)靥幚碛谰弥囟ㄏ颉?/li>
- 支持 Last-Modified 日期檢查從而避免在數(shù)據(jù)未改變的情況下重新下載數(shù)據(jù)。
- 支持 ETag hash 從而避免在數(shù)據(jù)未改變的情況下重新下載數(shù)據(jù)。
- 支持 gzip 壓縮從而在數(shù)據(jù)已經(jīng) 改變的情況下盡可能地減少傳輸帶寬。
總結(jié)
以上是生活随笔為你收集整理的《深入Python》-11. HTTP Web 服务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net Framework 4.0 中
- 下一篇: python 去除多个换行