平時我們也許用的更多的是requests模組,或者是requests_hml模組,但是他們都屬於阻塞型別的不支援非同步,速度很難提高,於是後來出現了非同步的grequests,開始了非同步網路請求,速度得到了大大的提升,但是今天我們要說的另外的一個比較非同步網路請求模組-aiohttp。
什麼是aiohhtp?
要學習一個模組,首先要知道這個模組都能做什麼,我們從官網上找到了關於aiohttp的解釋。
Asynchronous HTTP Client/Server for asyncio and Python.
Supports both Client and HTTP Server.
Supports both Server WebSockets and Client WebSockets out-of-the-box without the Callback Hell.
Web-server has Middlewares, Signals and pluggable routing.
大概意思是說aiohttp是一個非同步http網路模組分為了客戶端和服務端,同時支援websocket的使用,另外不用擔心在使用過程中多次回撥導致回撥地獄情況出現。
如何安裝?
在linux直接pip安裝應該沒什麼問題,一般都坑在windows系統上,我使用win10 64位系統直接安裝也是沒有出現什麼問題,
官網上推薦的是先按照cchardet,這是一個關於網頁編碼的模組直接執行下面命令即可
pip install cchardet複製程式碼
之後我們安裝
pip install aiohttp複製程式碼
後面的操作我們需要還要安裝一個協程模組asyncio。
pip install asyncio複製程式碼
ok,沒什麼問題安裝我們就到這裡了。
如何使用
我們先看一段程式碼,後面會對程式碼作出解釋。
import asyncio #匯入協程包import aiohttp #匯入aiohttp from lxml import html #匯入html解析模組headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"}async def getsource(url): conn=aiohttp.TCPConnector(verify_ssl=False)#防止ssl報錯 async with aiohttp.ClientSession(connector=conn) as session: #建立session async with session.get(url,headers=headers,timeout=60) as req: #獲得請求 if req.status==200: #判斷請求碼 source=await req.text()#使用await關鍵字獲取返回結果 print(html.fromstring(source).xpath("//title/text()")[0]) #獲取網頁標題 else: print("訪問失敗")if __name__=="__main__": full_urllist= ["https://www.baidu.com","https://www.cnblogs.com","https://www.jianshu.com"] event_loop = asyncio.get_event_loop() #建立事件迴圈 tasks = [getsource(url) for url in full_urllist] results = event_loop.run_until_complete(asyncio.wait(tasks))#等待任務結束複製程式碼
-----------------------------------------執行結果---------------------------------------------
部分內容已經在上面的程式碼中標註了,下面我們開始對上面的內容進行分析。
async/await
首先我們需要知道關鍵字async/await,在方法的面前加關鍵字async之後該方法就成了一個協程了,
一般await關鍵字使用的使用所在的方法必須帶有async,關於兩個關鍵字的演變,要從yield說起,
演變過程請參考http://python.jobbole.com/86069/,這裡只要知道是協程意思就行了。
首先我定義了聯結器並取消ssl安全驗證,我們使用verify_ssl使其等於False,預設是True的。
TCPConnector
因為有的網站請求的時候會驗證ssl證照,如果是自簽名的ssl證照會出錯。
conn=aiohttp.TCPConnector(verify_ssl=False)#防止ssl報錯複製程式碼
下面是關於TCPConnector的所有引數部分。
class aiohttp.TCPConnector(*, ssl=None, verify_ssl=True, fingerprint=None, use_dns_cache=True, ttl_dns_cache=10, family=0, ssl_context=None, local_addr=None, resolver=None, keepalive_timeout=sentinel, force_close=False, limit=100, limit_per_host=0, enable_cleanup_closed=False, loop=None)
limit
為了限制同時開啟的連線數量,我們可以將限制引數傳遞給聯結器:
conn = aiohttp.TCPConnector(limit=30)#同時最大進行連線的連線數為30,預設是100,limit=0的時候是無限制複製程式碼
limit_per_host:
conn = aiohttp.TCPConnector(limit_per_host=30)#預設是0複製程式碼
同一端點的最大連線數量。同一端點即(host, port, is_ssl)完全相同.
ClientSession
首先我們建立一個session物件,向下面這樣使用async宣告非同步,同時with上下文關鍵字 省去了關閉連線的程式碼,
async with aiohttp.ClientSession(connector=conn) as session: 複製程式碼
下面是ClientSession的所有引數,這裡用的比較多的是connector,headers,cookies其他的引數大家可以去自己探索一下。
class aiohttp.ClientSession(*, connector=None, loop=None, cookies=None, headers=None, skip_auto_headers=None, auth=None, json_serialize=json.dumps, version=aiohttp.HttpVersion11, cookie_jar=None, read_timeout=None, conn_timeout=None, timeout=sentinel, raise_for_status=False, connector_owner=True, auto_decompress=True, proxies=None
session.get
上面我們建立了session物件,然後我們就要進行請求具體的網站了。
async with session.get(url,headers=headers,timeout=60) as req: #獲得請求複製程式碼
這一步我們像使用requests那樣傳入headers引數並指定最大超時為60s。
ClientResponse
然後我們判斷請求是否情況,之後我們使用await req.text()獲取了網頁的原始碼,注意這裡必須使用await關鍵字來獲取協程的結果。然後我們使用了lxml模組獲取這三個網頁的title標題。
到目前為止我們只是定義了一個協程,並沒有真正的執行它接下來我們看看main方法是做什麼的。
full_urllist= ["https://www.baidu.com","https://www.cnblogs.com","https://www.jianshu.com"] event_loop = asyncio.get_event_loop() #建立時間迴圈 tasks = [getsource(url) for url in full_urllist] results = event_loop.run_until_complete(asyncio.wait(tasks))#等待任務結束複製程式碼
首先我定義了一個列表含有三個目標url,當前你可以定義更多。
asyncio.get_event_loop方法可以建立一個事件迴圈,然後使用run_until_complete將協程註冊到事件迴圈,並啟動事件迴圈。
協程物件不能直接執行,在註冊事件迴圈的時候,其實是run_until_complete方法將協程包裝成為了一個任務(task)物件。所謂task物件是Future類的子類。儲存了協程執行後的狀態,用於未來獲取協程的結果。
asyncio.ensure_future(coroutine) 和 loop.create_task(coroutine)都可以建立一個task,run_until_complete的引數是一個futrue物件。當傳入一個協程,其內部會自動封裝成task,task是Future的子類。isinstance(task, asyncio.Future)將會輸出True。
簡單的我們就瞭解到這吧,如果有什麼不懂的地方請留言大家一起討論。
更多文章關注公眾號:python學習開發
部落格園地址:www.cnblogs.com/c-x-a