1.請求異常處理 請求異常類型: 請求超時處理(timeout): 實現代碼: import requestsfrom requests import exceptions #引入exceptions A:請求超時 def timeout_request(): try: response = req ...
1.請求異常處理
請求異常類型:
請求超時處理(timeout):
實現代碼:
import requests
from requests import exceptions #引入exceptions
A:請求超時
def timeout_request():
try:
response = requests.get(build_uri('user/emails'), timeout=0.1)
except exceptions.Timeout as e:
print e.message
返回數據:HTTPSConnectionPool(host='api.github.com', port=443): Max retries exceeded with url: /user/emails (Caused by ConnectTimeoutError(<urllib3.connection.VerifiedHTTPSConnection object at 0x02BDEC50>, 'Connection to api.github.com timed out. (connect timeout=0.1)'))
B:未認證時訪問,報HTTPError
代碼:
def timeout_request():
response = requests.get(build_uri('user/emails'), timeout=10)
print response.text
print response.status_code
返回數據:
{"message":"Requires authentication","documentation_url":"https://developer.github.com/v3"}
401
C:拋出HTTPError異常信息
def timeout_request():
try:
response = requests.get(build_uri('user/emails'), timeout=10)
response.raise_for_status()
except exceptions.HTTPError as e:
print e.message
返回數據:
401 Client Error: Unauthorized for url: https://api.github.com/user/emails #401客戶端錯誤,沒有認證的情況下訪問url;
註意:以上的兩個小程式,通過使用try.....except機制,就合理的處理了發送請求時的各種各樣的“攔路虎”,依靠except分支可以幫助我們來處理異常,特別是在調用第三方服務的時候。
2.自定義Requests
requests庫進階部分官方功能變數名稱:http://www.python-requests.org/en/master/user/advanced/
翻譯上圖中紅色框中的內容:
會話對象允許你在發送請求的時候保留某些參數,它也能夠保留會話過程中發送請求的cookie,並且使用urllib3進程池,所以如果你向同一個功能變數名稱發送多個請求時,底層的TCP鏈接會被重用,由此會帶來一個很明顯的性能提高(參見HTTP持久鏈接)。
Session模塊的組成:
代碼實現:
def hard_requests():
'''構造請求
'''
from requests import Request, Session #引入Request Session
s = Session()
headers = {'User-Agent': 'fake1.3.4'}
req = Request('GET', build_uri('user/emails'), auth=('caolanmiao', 'key########'), headers=headers)
prepped = req.prepare() #使用prepare()方法準備url
print prepped.body
print prepped.headers
只有以上的代碼時的返回結果:
None
{'Authorization': 'Basic aW1vb2NkZW1vOmltb29jZGVtbzEyMw==', 'User-Agent': 'fake1.3.4'}
可見,請求還沒有發送出去
'''發送請求
'''
resp = s.send(prepped, timeout=5) #發送請求,使用send()方法
print resp.status_code
print resp.request.headers
print resp.text
加上以上代碼後的返回結果:
None
{'Authorization': 'Basic aW1vb2NkZW1vOmltb29jZGVtbzEyMw==', 'User-Agent': 'fake1.3.4'}
200
{'Authorization': 'Basic aW1vb2NkZW1vOmltb29jZGVtbzEyMw==', 'User-Agent': 'fake1.3.4'}
[{"email":"[email protected]","primary":true,"verified":false,"visibility":"private"},{"email":"[email protected]","primary":false,"verified":false,"visibility":null},
可見,此時請求才發送成功。
通過上面的代碼可以看出,使用自定義的方法構造請求,可以隨時控制請求的發送時機。
3.處理響應
響應基本API:
代碼舉例:
# -*- coding: utf-8 -*-
import requests
response = requests.get('https://api.github.com')
print "狀態碼,具體解釋"
print response.status_code, response.reason
print "頭部信息"
print response.headers
print "URL 信息"
print response.url
print "redirect 信息"
print response.history
print "耗費時長"
print response.elapsed
print "request 信息"
print response.request.method
print "編碼信息"
print response.encoding
print "消息主體內容: byte"
print response.content, type(response.content)
print "消息主體內容: 解析"
print response.text, type(response.text)
print "消息主體內容"
print response.json(), type(response.json())
有關HTTP狀態碼(status_code)的功能變數名稱,可以關註維基百科:https://zh.wikipedia.org/zh/HTTP%E7%8A%B6%E6%80%81%E7%A0%81
常見的status_code和reason:
2xx成功:這一類型的狀態碼,代表請求已成功被伺服器接收、理解、並接受。
200 OK 請求已成功,請求所希望的響應頭或數據體將隨此響應返回
201 Created 請求已經被實現,而且有一個新的資源已經依據請求的需要而建立
202 Accepted 伺服器已接受請求,但尚未處理
204 No Content 伺服器成功處理了請求,沒有返回任何內容
3xx重定向:這類狀態碼代表需要客戶端採取進一步的操作才能完成請求。通常,這些狀態碼用來重定向,後續的請求地址(重定向目標)在本次響應的Location域中指明
301 Moved Permanently 被請求的資源已永久移動到新位置,並且將來任何對此資源的引用都應該使用本響應返回的若幹個URI之一。如果可能,擁有鏈接編輯功能的客戶端應當自動把請求的地址修改為從伺服器反饋回來的地址。除非額外指定,否則這個響應也是可緩存的
302 Found 要求客戶端執行臨時重定向(原始描述短語為“Moved Temporarily”)。由於這樣的重定向是臨時的,客戶端應當繼續向原有地址發送以後的請求。只有在Cache-Control或Expires中進行了指定的情況下,這個響應才是可緩存的
304 Not Modified 表示資源未被修改,因為請求頭指定的版本If-Modified-Since或If-None-Match。在這種情況下,由於客戶端仍然具有以前下載的副本,因此不需要重新傳輸資源
4xx客戶端錯誤:這類的狀態碼代表了客戶端看起來可能發生了錯誤,妨礙了伺服器的處理。除非響應的是一個HEAD請求,否則伺服器就應該返回一個解釋當前錯誤狀況的實體,以及這是臨時的還是永久性的狀況。這些狀態碼適用於任何請求方法。瀏覽器應當向用戶顯示任何包含在此類錯誤響應中的實體內容。
400 Bad Request 由於明顯的客戶端錯誤(例如,格式錯誤的請求語法,太大的大小,無效的請求消息或欺騙性路由請求),伺服器不能或不會處理該請求
401 Unauthorized 類似於403 Forbidden,401語義即“未認證”,即用戶沒有必要的憑據
403 Forbidden 沒有許可權;伺服器已經理解請求,但是拒絕執行它。與401不同的是,身份驗證並不能提供任何幫助,而且這個請求也不應該被重覆提交
404 Not Found 請求失敗,請求所希望得到的資源未被在伺服器上發現,但允許用戶的後續請求
5XX:伺服器錯誤,表示伺服器無法完成明顯有效的請求
500 Internal Server Error 通用錯誤消息,伺服器遇到了一個未曾預料的狀況,導致了它無法完成對請求的處理。沒有給出具體錯誤信息
502 Bad Gateway 作為網關或者代理工作的伺服器嘗試執行請求時,從上游伺服器接收到無效的響應
503 Service Unavailable 由於臨時的伺服器維護或者過載,伺服器當前無法處理請求。這個狀況是暫時的,並且將在一段時間以後恢復
通過dir(response),可以查看response的全部API。
4.下載圖片/文件
舉例:通過下麵代碼實現下載圖片(百度“github”圖片中的第一個),下圖是實現流程圖
# -*- coding: utf -*-
import requests
def download_image():
"""demo: 下載圖片, 文件
"""
# 偽造headers信息
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36'} #獲取許可權,使用Firefox瀏覽器
# 限定url
url = "http://img3.imgtn.bdimg.com/it/u=2228635891,3833788938&fm=21&gp=0.jpg"
response = requests.get(url, headers=headers, stream=True) #stream=True 流傳輸
# 打開文件
with open('demo.jpg', 'wb') as fd:
# 每128寫入一次
for chunk in response.iter_content(128):
fd.write(chunk)
def download_image_improved():
"""demo: 下載圖片
"""
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36'}
url = "http://img3.imgtn.bdimg.com/it/u=2228635891,3833788938&fm=21&gp=0.jpg"
response = requests.get(url, headers=headers, stream=True)
from contextlib import closing
with closing(requests.get(url, headers=headers, stream=True)) as response: #把打開的流stream關閉,以節省資源
with open('demo1.jpg', 'wb') as fd:
for chunk in response.iter_content(128):
fd.write(chunk)
download_image_improved()
如下圖,demo.jpg文件已經成功下載到本地了
5.事件鉤子
事件鉤子(event hooks)模型
# -*- coding: utf-8 -*-
import requests
def get_key_info(response, *args, **kwargs):
"""回調函數
"""
print response.headers['Content-Type']
def main():
"""主程式
"""
requests.get('https://api.github.com', hooks=dict(response=get_key_info))
main()
返回數據:
application/json; charset=utf-8
利用事件鉤子的方法可以使得request和response分開,特別是當代碼很多的時候,方便管理。
6.Request庫--HTTP認證
前面所講的發送一個請求,得到響應,其實有一個簡單的假設,就是通過HTTP通訊,我們訪問的所有資源都是可見的,實際上很多時候,為了提高安全性,服務端要驗證請求的來源,只有通過驗證的請求,服務端才會響應。最簡單基本認證的模式,如下圖:
對於受保護的服務,在發送請求的時候就要加上一個requests庫里的認證參數:auth
實驗代碼:
# -*- coding: utf-8 -*-
import requests
BASE_URL='https://api.github.com'
def construct_url(end_point):
return '/'.join([BASE_URL,end_point])
def basic_auth():
"""基本認證
"""
response=requests.get(construct_url('user'),auth=('caolanmiao','key######')) #添加auth參數,完成認證
print response.text
print response.request.headers
basic_auth()
返回數據:
{"login":"caolanmiao","id":22490616,"avatar_url":"https://avatars0.githubusercontent.com/u/22490616?v=4","gravatar_id":"","url":"https://api.github.com/users/caolanmiao","html_url":"https://github.com/caolanmiao","followers_url":"https://api.github.com/users/caolanmiao/followers","following_url":"https://api.github.com/users/caolanmiao/following{/other_user}","gists_url":"https://api.github.com/users/caolanmiao/gists{/gist_id}","starred_url":"https://api.github.com/users/caolanmiao/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caolanmiao/subscriptions","organizations_url":"https://api.github.com/users/caolanmiao/orgs","repos_url":"https://api.github.com/users/caolanmiao/repos","events_url":"https://api.github.com/users/caolanmiao/events{/privacy}","received_events_url":"https://api.github.com/users/caolanmiao/received_events","type":"User","site_admin":false,"name":"Yannan.Jia","company":null,"blog":"","location":"Pecking","email":"[email protected]","hireable":null,"bio":"Software QA Engineer","public_repos":1,"public_gists":0,"followers":0,"following":1,"created_at":"2016-09-28T06:00:27Z","updated_at":"2017-08-19T09:27:39Z","private_gists":0,
"total_private_repos":0,"owned_private_repos":0,"disk_usage":0,"collaborators":0,"two_factor_authentication":false,"plan":{"name":"free","space":976562499,"collaborators":0,"private_repos":0}}
{'Authorization': 'Basic Y2FvbGFubWlhbzpqaWEyMjE1NDkw', 'Connection': 'keep-alive', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'User-Agent': 'python-requests/2.18.3'}
返回的數據中,可以看到,添加auth參數後,在request的headers中加了一串Authorization,Basic後面的這一串碼“Y2FvbGFubWlhbzpqaWEyMjE1NDkw”是什麼呢?
其實這一串碼是base64格式的,解碼後就是我的用戶名和密碼('caolanmiao','key######')。
所以這種基本形式的認證是把用戶名和密碼放在request的headers中,使用任何語言(比如python)都可以對已經編碼後的用戶名和密碼進行解碼,可見,具備一定的安全性,但是安全性並不夠強。
另一種安全性更強的認證---OAUTH認證
比如一個app在登錄的時候,提供了通過github賬號來快速登錄的入口,那麼完成登錄的流程就如下圖所示:
實驗代碼:
# -*- coding:utf-8 -*-
import requests
BASE_URL='https://api.github.com'
def construct_url(end_point):
return '/'.join([BASE_URL,end_point])
def basic_oauth():
headers={'Authorization':'token dd6322fa6c57a548268453dc245cbcdc352a7811'} #通過token完成認證
# user/emails
response=requests.get(construct_url('user/emails'),headers=headers)
print response.request.headers
print response.text
print response.status_code
7.Request庫--Proxy(代理)
Heroku這個伺服器(相當於國內的“阿裡雲”)對於國外的資源是可見的,比如是可以直接訪問facebook的;Socks服務是一種在會話層的協議,類似於HTTP協議;
實現代碼:
import requests
proxies={'http':'socks5://127.0.0.1080','https':'socks5://127.0.0.1080'} #定義代理
url='https://www.facebook.com'
response=requests.get(url,proxies=proxies,timeout=10) #添加代理
8.Request庫--Session和Cookie
大家都知道http協議的請求和下一個請求之間是獨立的、沒有關係的。但是經常有些時候我們需要使得請求和上一個請求有關係,比如“在購物的情景中,已經登錄了賬戶,把商品加入購物車中,不能出現發出一個新請求後,購物車中的商品清空了的情況”。
簡而言之,session是存儲在伺服器端的,來保存登錄信息;cookie是保存在瀏覽器中來存儲的。
cookie存儲的方式如圖1:
cookie存儲的方式缺點:1.每次發送請求都帶著cookie,造成了帶寬特別大,特別占用網路請求;2.cookie如果在瀏覽器端是明文存儲的話,容易解析時,是可以偽造的,很不安全。
那麼鑒於cookie存儲方式的不足,session存儲的方式應運而生。
session存儲的方式如圖2:
session存儲的方式優點:1. 伺服器返回的cookie-session-id相比於整個cookie是很小的,節省帶寬,減少了網路傳輸的壓力;2.伺服器把session存儲在資料庫或者radis(記憶體)中,實現了本地化,相比於cookie存儲方式的每次在網路中發送整個cookie更安全。
看到的同學也可以利用github上的api自己試試。
尊重博客園原創精神,請勿轉載!