requests
requests是個HTTPClient庫,相比於urllib,urllib2等模塊比更加簡潔易用
■ get請求
作為示例,講一下關於requests如何發起並處理一個get請求
r = requests.get("http://www.baidu.com") #可以加入timeout參數來設置超時
r是一個Response對象,可以用r查看很多信息
如r.status_code 查看本次請求的http返回碼
r.headers 頭部信息(是個類字典對象)
r.url 本次請求用的url
r.encoding 編碼方式
r.text 正文
r.content 正文
*text和content的區別是text是以unicode的形式存在內存中的,而content是把text用r.encoding編碼進行encode之后的產物。所以當返回內容是html文本時,type(text)是unicode而type(content)是str。當返回的是類似於圖片這樣的東西時,編碼可以是base64這種二進制編碼,所以如果要下載一張圖或一個文件時,應該寫入本地文件的也是content而不是text。
再啰嗦一句。。下載的如果是文本文件的話寫入方式似乎不帶b也行得通,但是如果想下載圖片等其他文件就必須是二進制寫入了,要不然會打不開的。
r.cookies 獲得返回內容中的cookie內容,若想在請求中加入cookie的話只要在參數中加上cookies={...}即可
r對象還有一些方法:
r.raise_for_status() #當返回碼不是200的時候拋出一個HTTPError異常
另外,get方法還可以把一個字典整合到url中去形成一個帶參數的GET請求。比如
par = {'wd':'word'} r = requests.get("http://www.baidu.com/s",params=par) >>>r.url u'http://www.baidu.com/s?wd=word'
如果返回的是個json串,那么r還可以用json()方法構造出這個串對應的python字典
r = requests.get("url") #假設返回的json是 {"code":"200","msg":"OK"} res = r.json() print res.get("code"),res.get("msg") #得到的就是200 OK
除了params參數之外,get方法還可以有proxies參數以設置代理;headers參數來偽造請求頭部(現在很多網站因為防止爬蟲等一些原因會檢查請求頭部,如果不偽造頭部的話很可能會出現500狀態的返回)
比如headers={'User-Agent':'test'};r = requests.get(url,headers=headers)
加入cookies={一個字典}來增加請求中的cookie信息
■ POST
POST請求用post方法,和get類似的設置。
在加上post的數據時注意參數是data不是params了!! ==> r = requests.post(url,data=par)
■ 請求https地址
請求https地址時,由於涉及到SSL證書的驗證等工作,和普通的HTTP請求還不太一樣。比如get方法中默認會帶上參數verify=True表示請求前要先驗證對方的SSL公鑰證書。如果確認不用驗證可以將此參數置為False,這樣就不用驗證了。
有時候驗證會報錯hostname does not match xxxx,這可能是因為你請求的url的域名,其上面的SSL公鑰證書還沒有被導入當前主機,一個解決辦法是將證書導出后存放在某個地方,然后verify參數寫指向證書文件的路徑。證書導出隨各種瀏覽器、工具不同而不同。比如火狐的話右鍵頁面--查看頁面信息--安全--查看證書詳細,中可以選擇導出。
如果選擇了正確的證書之后,請求仍然報錯:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed,這個錯誤可能是由於openssl版本過低。
■ 上傳帶中文名的文件
用requests的post方法可以方便地上傳文件,一般的做法是這樣的:
import requests data = { 'other_data': 'value' } fi = open('test.jpg','rb') files = { 'test.jpg': ('test.jpg',fi,'image/jpg') } url = 'http://127.0.0.1/uploadtest/' res = requests.post(url, data=data, files=files) fi.close() print res.status_code,res.content
可以看到,除了data參數之外添加了files參數。files參數的內容是一個字典,字典中的key可以寫file或者其他的什么內容。其對標的是網頁表單提交時input標簽中name屬性的值。一般而言只要保證多個上傳文件的鍵值對中,key沒有重復的即可。value是一個tuple,其中各項元素的意義是(filename, file_object, content_type, ...)等,后面還有,不常用就沒寫。文件名是定義在這里的。如果要上傳多個文件,那么就在這個字典中多加幾個鍵值對。需要注意文件對象fi一直到post方法被調用后才能close,否則會報錯。
這里面隱藏着一個問題,當上傳的文件名是中文名的時候后端可能會獲取不到文件的內容,比如用flask做后端時看一下request.files可以看到是個空字典。網上給出了一些解決方法(https://segmentfault.com/q/1010000002633223/a-1020000002657633),比如修改requests中packages/urllib3/fields.py中的一些源代碼,嘗試了一下確實可以解決。不過修改源碼不是很好的解決方法,如果后端也是自己寫的話那么可以參考這篇教程中的解決方法三(http://www.mamicode.com/info-detail-1499317.html)。
我的解決辦法就是(由於服務器端也是我自己寫的),在data中加上filename字段指出中文的,實際的文件名。然后在files中的value那個tuple中的第一項(注意,是{'test.jpg': ('test.jpg', fi)}這個文件名)那個文件名改成file_name或者其他不帶中文的名字。在服務端的代碼中,首先按照提供的非中文文件名保存下來,然后改名為中文文件名;或者直接按照提供的中文文件名保存,這個取決於后端的規則和寫法。比如通過flask搭建的一個服務接受中文名文件上傳:
#####服務端代碼##### @app.route('/uploadtest/',methods=['POST']) def upload_file(): filenames = request.form.get('filename').split(',') count = 0 for _,storage in request.files.iteritems(): storage.save(os.path.join(BASEDIR,'upload',filenames[count])) count += 1 # ... #####客戶端代碼##### from collections import OrderedDict data = { 'filename': 'a.txt,b.jpg,c.exe' } f1,f2,f3 = open('a.txt','rb'),open('b.jpg','rb'),open('c.exe','rb') files['a.txt'] = ('f1',f1) files['b.jpg'] = ('f2',f2) files['c.exe'] = ('f3',f3) res = requests.post('http://localhost/uploadtest/',data=data,files=files) f1.close();f2.close();f3.close()
用了OrderedDict的原因是為了讓filename參數得到的文件名列表和上傳文件通過順序一一對應,避免文件名和文件錯位。
urllib2
urllib2是比requests更加貼近底層,可以實現更加個性化的HTTPClient?
■ 基本用法
req = urllib2.Request('...') #用一個url來建立一個Request對象,這個url可以是http協議的也可以是ftp協議的 response = urllib2.urlopen(req) #打開這一request對象 page = response.read() #response可以像一個文件一樣read內容
如果是使用http協議進行通信的話,用戶可以通過POST或GET方法來豐富自己的http請求方式,比如自己附上一些表單data,或者添加http的headers信息
① 增加表單data
首先需要一個python字典來抽象化要發送的數據,然后一定要用urllib.urlencode將這個字典處理成urlopen可以識別的東西(好像就是個key=value&key2=value2這樣的,跟在get請求的url后面的那種形式)。然后在創建Request對象的時候就有兩種方法,分別對應着POST請求和GET請求
par = {'wd':'word'} processed_par = urllib.urlencode(par) #對字典進行處理,用到的是urllib中的urlencode方法 req = urllib2.Request('url',data=processed_par) #寫個參數data=處理好的數據這種方式呢對應的是POST請求 req = urllib2.Request(''url'+'?'+processed_par) #直接加上處理好的數據是指GET請求的方式(不要忘了中間的問號!!! urllib2.urlopen(req) #打開Request對象來進行訪問
② 增加header信息
header即頭部,http頭部屬於http訊息的一部分,其邏輯形式大致是一個字典,內容包括客戶端可接受的內容類型,編碼,語言,證書信息,用戶所用代理(瀏覽器的牌子)是什么等等。從應用邏輯上分,header又被分成了general header,request header,response header和entity header四部分
因為urllib2主要是個http客戶端庫,所以主要關注request header的部分
如以下做法可以為header添加上User-Agent信息(用戶的瀏覽器是什么)
url = 'http://www.xxxxxxxx...' userAgent = 'Mozilla 4.0 xxxxx' #模擬火狐瀏覽器的信息 headers = {'User-Agent':userAgent} #設置header字典 data = urllib.urlencode(...) #POST請求的表單數據 req = urllib2.Request(url,data=data,headers=headers) res = urllib2.urlopen(req) #這就是打開了帶有自己定義的頭部信息的請求了
■ 零碎的記錄
*關於錯誤 URLError & HTTPerror
這兩個錯誤是在應用http客戶端庫時常見的錯誤。首先,HTTPerror是URLError的一個子類,即發生HTTPError時必然發生URLError
另外,URLError通常是指沒有網絡連接(沒有特定路由到指定服務器),或者服務器不存在的錯誤。對應的,HTTPError指的是那些服務器指向正確並且連接OK,但是無法完成請求的情況。
URLError對象通常都有reason屬性,是個tuple,[0]是錯誤號,[1]是錯誤信息,所以在except URLError的時候可以print e.reason來查看具體信息。類似的HTTPError則有一個code屬性,記錄的是http返回碼,如眾所周知的404代表找不到文件,401代表需要驗證,500代表服務器內部出錯等等。
*關於url的解碼和編碼
有些符號如'{','\'是無法被識別的,需要對其進行編碼成%7B,%5C之類的某種我也說不太清到底是什么碼的碼,可以用urllib.quote(url)對url進行一個編碼,同樣的urllib.unquote(url)是把編好碼的url解碼成人看得懂的形式。
IPy
一個小模塊。。沒地方放了,反正這篇好像都是跟網絡沾邊就放着把。
本來我想找個能驗證ip地址合法性的模塊,而找到的這個IPy似乎功能要更加強大一些。IPy下載地址https://github.com/haypo/python-ipy/
● 對ip的處理
首先用IPy.IP來建立一個IP對象
ip = IP('x.x.x.x') #在構造IP對象時就已經對ip合法性做出判斷,如果不對就報錯了。我最開始的需求就這么實現了。。
IP對象有__repr__方法,可以直接被print成字符串。
ip.make_net('子網掩碼') 返回這個ip所在的網段地址,比如
ip = IP(127.0.0.1) net = ip.make_net('255.0.0.0') print net ##輸出結果就是 IP('127.0.0.0/8')
print ip in net ##還可以直接用 in 語句來判斷某個ip在不在一個網段里
● 對網段地址的處理
在構造的時候,如果寫的是一個網絡,那么IP類會自動將其識別成一個網絡
net = IP('127.0.0.0/24')
這個net對象時iterable的,可以迭代出這個網段里所有的ip地址,用len(net)自然就可以看到這個網段里一共能有多少ip了
在print net的是時候同樣會打印出來這個網絡,只是表示一個網絡有多種方法,到底取哪一種呢?
可以用net.strNormal(n)這個方法來改變打印出來的格式,比如上面那個net,如果設置了n是0的話只打印出127.0.0.0(網絡地址)
n為1的場合 打印出類似於127.0.0.0/24 這種表現形式
n為2的場合 打印出127.0.0.0/255.255.255.0 的形式
n為3的場合 打印出127.0.0.0 - 127.0.0.255 的形式