urllib模塊提供的上層接口,使我們可以像讀取本地文件一樣讀取www和ftp上的數(shù)據(jù)。每當(dāng)使用這個模塊的時候,老是會想起公司產(chǎn)品的客戶端,同事用C++下載Web上的圖片,那種“痛苦”的表情。我以前翻譯過libcurl教程,這是在C/C++環(huán)境下比較方便實用的網(wǎng)絡(luò)操作庫,相比起 libcurl,Python的urllib模塊的使用門檻則低多了。可能有些人又會用效率來批評Python,其實在操作網(wǎng)絡(luò),或者在集群交互的時候, 語言的執(zhí)行效率絕不是瓶頸。這種情況下,一個比較好的方法是,將python嵌入到C/C++中,讓Python來完成一些不是核心的邏輯處理。又扯遠(yuǎn)了,廢話少說,開始urllib之旅吧~~ (前幾天我用這個模塊寫了個蜘蛛,感興趣的同學(xué)可以在以前的博客中找到代碼)
先看一個例子,這個例子把Google首頁的html抓取下來并顯示在控制臺上:
view plaincopy to clipboardprint?
1. # 別驚訝,整個程序確實只用了兩行代碼
2. import urllib
3. print urllib.urlopen('http://www.google.com').read()
# 別驚訝,整個程序確實只用了兩行代碼
import urllib
print urllib.urlopen('http://www.google.com').read()
urllib.urlopen(url[, data[, proxies]]) :
創(chuàng)建一個表示遠(yuǎn)程url的類文件對象,然后像本地文件一樣操作這個類文件對象來獲取遠(yuǎn)程數(shù)據(jù)。參數(shù)url表示遠(yuǎn)程數(shù)據(jù)的路徑,一般是網(wǎng)址;參數(shù)data表示以post方式提交到url的數(shù)據(jù)(玩過web的人應(yīng)該知道提交數(shù)據(jù)的兩種方式:post與get。如果你不清楚,也不必太在意,一般情況下很少用到這個參數(shù));參數(shù)proxies用于設(shè)置代理(這里不詳細(xì)講怎么使用代理,感興趣的看客可以去翻閱Python手冊urllib模塊)。urlopen返回 一個類文件對象,他提供了如下方法:
* read() , readline() , readlines() , fileno() , close() :這些方法的使用方式與文件對象完全一樣;
* info():返回一個httplib.HTTPMessage 對象,表示遠(yuǎn)程服務(wù)器返回的頭信息;
* getcode():返回Http狀態(tài)碼。如果是http請求,200表示請求成功完成;404表示網(wǎng)址未找到;
* geturl():返回請求的url;
下面來擴充一下上面的例子,看官可以運行一下這個例子,加深對urllib的印象:
view plaincopy to clipboardprint?
1. google = urllib.urlopen('http://www.google.com')
2. print 'http header:\n', google.info()
3. print 'http status:', google.getcode()
4. print 'url:', google.geturl()
5. for line in google: # 就像在操作本地文件
6. print line,
7. google.close()
google = urllib.urlopen('http://www.google.com')
print 'http header:\n', google.info()
print 'http status:', google.getcode()
print 'url:', google.geturl()
for line in google: # 就像在操作本地文件
print line,
google.close()
urllib.urlretrieve(url[, filename[, reporthook[, data]]]):
urlretrieve方法直接將遠(yuǎn)程數(shù)據(jù)下載到本地。參數(shù)filename指定了保存到本地的路徑(如果未指定該參數(shù),urllib會生成一個臨時文件來保存數(shù)據(jù));參數(shù)reporthook是一個回調(diào)函數(shù),當(dāng)連接上服務(wù)器、以及相應(yīng)的數(shù)據(jù)塊傳輸完畢的時候會觸發(fā)該回調(diào)。我們可以利用這個回調(diào)函 數(shù)來顯示當(dāng)前的下載進(jìn)度,下面的例子會展示。參數(shù)data指post到服務(wù)器的數(shù)據(jù)。該方法返回一個包含兩個元素的元組(filename, headers),filename表示保存到本地的路徑,header表示服務(wù)器的響應(yīng)頭。下面通過例子來演示一下這個方法的使用,這個例子將新浪首頁的html抓取到本地,保存在D:\sina.html文件中,同時顯示下載的進(jìn)度。
view plaincopy to clipboardprint?
1. def cbk(a, b, c):
2. '''''回調(diào)函數(shù)
3. @a: 已經(jīng)下載的數(shù)據(jù)塊
4. @b: 數(shù)據(jù)塊的大小
5. @c: 遠(yuǎn)程文件的大小
6. '''
7. per = 100.0 * a * b / c
8. if per > 100:
9. per = 100
10. print '%.2f%%' % per
11.
12. url = 'http://www.sina.com.cn'
13. local = 'd:\\sina.html'
14. urllib.urlretrieve(url, local, cbk)
def cbk(a, b, c):
'''回調(diào)函數(shù)
@a: 已經(jīng)下載的數(shù)據(jù)塊
@b: 數(shù)據(jù)塊的大小
@c: 遠(yuǎn)程文件的大小
'''
per = 100.0 * a * b / c
if per > 100:
per = 100
print '%.2f%%' % per
url = 'http://www.sina.com.cn'
local = 'd:\\sina.html'
urllib.urlretrieve(url, local, cbk)
上面介紹的兩個方法是urllib中最常用的方法,這些方法在獲取遠(yuǎn)程數(shù)據(jù)的時候,內(nèi)部會使用URLopener或者 FancyURLOpener類。作為urllib的使用者,我們很少會用到這兩個類,這里我不想多講。如果對urllib的實現(xiàn)感興趣, 或者希望urllib支持更多的協(xié)議,可以研究這兩個類。在Python手冊中,urllib的作者還列出了這個模塊的缺陷和不足,感興趣的同學(xué)可以打開 Python手冊了解一下。
urllib中還提供了一些輔助方法,用于對url進(jìn)行編碼、解碼。url中是不能出現(xiàn)一些特殊的符號的,有些符號有特殊的用途。我們知道以get方式提交數(shù)據(jù)的時候,會在url中添加key=value這樣的字符串,所以在value中是不允許有'=',因此要對其進(jìn)行編碼;與此同時服務(wù)器接收到這些參數(shù)的時候,要進(jìn)行解碼,還原成原始的數(shù)據(jù)。這個時候,這些輔助方法會很有用:
* urllib.quote(string[, safe]):對字符串進(jìn)行編碼。參數(shù)safe指定了不需要編碼的字符;
* urllib.unquote(string) :對字符串進(jìn)行解碼;
* urllib.quote_plus(string [ , safe ] ) :與urllib.quote類似,但這個方法用'+'來替換' ',而quote用'%20'來代替' '
* urllib.unquote_plus(string ) :對字符串進(jìn)行解碼;
* urllib.urlencode(query[, doseq]):將dict或者包含兩個元素的元組列表轉(zhuǎn)換成url參數(shù)。例如 字典{'name': 'dark-bull', 'age': 200}將被轉(zhuǎn)換為"name=dark-bull&age=200"
* urllib.pathname2url(path):將本地路徑轉(zhuǎn)換成url路徑;
* urllib.url2pathname(path):將url路徑轉(zhuǎn)換成本地路徑;
用一個例子來體驗一下這些方法吧~~:
view plaincopy to clipboardprint?
1. data = 'name = ~a+3'
2.
3. data1 = urllib.quote(data)
4. print data1 # result: name%20%3D%20%7Ea%2B3
5. print urllib.unquote(data1) # result: name = ~a+3
6.
7. data2 = urllib.quote_plus(data)
8. print data2 # result: name+%3D+%7Ea%2B3
9. print urllib.unquote_plus(data2) # result: name = ~a+3
10.
11. data3 = urllib.urlencode({ 'name': 'dark-bull', 'age': 200 })
12. print data3 # result: age=200&name=dark-bull
13.
14. data4 = urllib.pathname2url(r'd:\a\b\c\23.php')
15. print data4 # result: ///D|/a/b/c/23.php
16. print urllib.url2pathname(data4) # result: D:\a\b\c\23.php
data = 'name = ~a+3'
data1 = urllib.quote(data)
print data1 # result: name%20%3D%20%7Ea%2B3
print urllib.unquote(data1) # result: name = ~a+3
data2 = urllib.quote_plus(data)
print data2 # result: name+%3D+%7Ea%2B3
print urllib.unquote_plus(data2) # result: name = ~a+3
data3 = urllib.urlencode({ 'name': 'dark-bull', 'age': 200 })
print data3 # result: age=200&name=dark-bull
data4 = urllib.pathname2url(r'd:\a\b\c\23.php')
print data4 # result: ///D|/a/b/c/23.php
print urllib.url2pathname(data4) # result: D:\a\b\c\23.php