asyncio異步攜程模塊python
在python3.4以後新增了asyncio模塊,能夠幫咱們檢測IO(只能是網絡IO【HTTP鏈接就是網絡IO操做】),實現應用程序級別的切換(異步IO)。注意:asyncio只能發tcp級別的請求,不能發http協議。 flask
- 異步IO:所謂「異步 IO」,就是你發起一個 網絡IO 操做,卻不用等它結束,你能夠繼續作其餘事情,當它結束時,你會獲得通知。 - 實現方式:單線程+協程實現異步IO操做。 - 異步協程用法 協程的實現,從 Python 3.4 開始,Python 中加入了協程的概念,但這個版本的協程仍是以生成器對象爲基礎的,在 Python 3.5 則增長了 async/await,使得協程的實現更加方便。首先須要瞭解下面幾個概念: event_loop:事件循環,至關於一個無限循環,咱們能夠把一些函數註冊到這個事件循環上,當知足條件發生的時候,就會調用對應的處理方法。 coroutine:中文翻譯叫協程,在 Python 中常指代爲協程對象類型,咱們能夠將協程對象註冊到時間循環中,它會被事件循環調用。咱們可使用 async 關鍵字來定義一個方法,這個方法在調用時不會當即被執行,而是返回一個協程對象。 task:任務,它是對協程對象的進一步封裝,包含了任務的各個狀態。 future:表明未來執行或沒有執行的任務的結果,實際上和 task 沒有本質區別。 從 Python 3.5 出現的async/await 關鍵字,專門用於定義協程。其中,async 定義一個協程,await 用來掛起阻塞方法的執行。
aiohttp異步網絡請求模塊服務器
aiohttp
是基於asyncio
實現的HTTP框架,pip install aiohttp。 網絡
1 import aiohttp 2 3 #簡單的基本架構: 4 async def request(url): 5 async with aiohttp.ClientSession() as s: 6 #s.get/post和requests中的get/post用法幾乎同樣:url,headers,data/prames 7 #在s.get中若是使用代理操做:proxy="http://ip:port" 8 async with await s.get(url) as response: 9 #獲取字符串形式的響應數據:response.text() 10 #獲取byte類型的:response.read() 11 page_text = await response.text() 12 return page_text
單線程+多任務異步協程架構
協程:協程對象。使用async關鍵字修飾一個函數的定義(特殊的函數),當該特殊的函數被調用後,就能夠返回一個協程對象。當函數調用後,函數內部的實現語句不會被當即執行。協程就是一個用async關鍵字修飾的特殊函數。協程定義:async def func():pass,建立協程對象:c=func() app
1 ''' 2 協程是一個用async關鍵字修飾的特殊函數; 3 在這個特殊函數內不容許出現不支持異步模塊的代碼,不然異步非阻塞將失效; 4 在特殊函數中遇到阻塞的代碼前必須使用await關鍵字手動掛起(後續示例) 5 6 ''' 7 #定義一個協程 8 async def func(): 9 print('一個協程對象') 10 11 #建立協程對象 12 c=func()
任務對象:本質上就是對協程對象進一步封裝,比如是一個特殊函數。任務對象task=asyncio.ensure_future(c)能夠綁定一個回調函數:task.add_done_callback(func_callback)-----func_callback回調函數只能接受task任務對象執行結果返回值,類比進程池/線程池中submit提交任務後的回調函數使用! 框架
1 ''' 2 任務對象是對協程對象進行封裝後的一個特殊函數; 3 任務對象能夠綁定一個回調函數; 4 ''' 5 import asyncio 6 #定義一個協程 7 async def func(n): 8 print(n) 9 10 #定義任務對象的回調函數:只能接受以個參數,該參數爲任務對象執行結果返回值 11 def call_back(task): 12 print(task.result()) 13 14 #建立協程對象 15 c=func(-1) 16 17 18 #建立任務對象 19 task=asyncio.ensure_future(c) 20 #任務對象綁定回調函數 21 task.add_done_callback(call_back) 22 23 # 建立多個任務對象併爲其綁定回調函數 24 task_list=[] 25 for i in range(5): 26 c=func(i) 27 task=asyncio.ensure_future(c) 28 task.add_done_callback(task) 29 task_list.append(task)
事件循環:事件循環對象的建立loop=asyncio.get_event_loop()。必須將任務對象註冊到事件循環對象中,自動開啓事件循環對象,在執行任務對象的時候是基於異步的。單個任務對象的註冊:loop.run_until_complete(task);多個任務對象的註冊:loop.run_and_complete(asyncio.wait(task_list))。 異步
1 ''' 2 建立事件循環對象對任務對象進行註冊調用; 3 註冊單個任務對象/註冊多個任務對象並自動調用執行; 4 5 ''' 6 import asyncio 7 #定義一個協程 8 import time 9 10 11 async def func(n): 12 await asyncio.sleep(2)#必須使用支持異步模塊的代碼,同時在遇到阻塞的代碼前必須使用await手動掛起 13 print(n) 14 return n 15 16 #定義任務對象的回調函數:只能接受以個參數,該參數爲任務對象執行結果返回值 17 def call_back(task): 18 print(task.result()) 19 20 #建立協程對象 21 c=func(-1) 22 23 start=time.time() 24 #建立任務對象 25 task0=asyncio.ensure_future(c) 26 #任務對象綁定回調函數 27 task0.add_done_callback(call_back) 28 29 # 建立多個任務對象併爲其綁定回調函數 30 task_list=[] 31 for i in range(5): 32 c=func(i) 33 task=asyncio.ensure_future(c) 34 task.add_done_callback(call_back) 35 task_list.append(task) 36 37 #建立事件循環對象 38 loop=asyncio.get_event_loop() 39 #註冊單個任務對象 40 loop.run_until_complete(task0) 41 #註冊多個任務對象 42 loop.run_until_complete(asyncio.wait(task_list)) 43 44 loop.close() 45 print(time.time()-start)#2.00264573097229
注意事項:async
單線程多任務異步爬蟲實例tcp
flask服務器腳本構建:
import time from flask import Flask app=Flask(__name__) @app.route('/<id>') def func1(id): time.sleep(2) return id if __name__ == '__main__': app.run()
單線程多任務異步數據採集代碼:
1 import time 2 import asyncio # pip install asyncio 3 import aiohttp #pip install aiohttp 4 headers = { 5 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36' 6 } 7 #建立urls列表 8 urls = [] 9 for i in range(500): 10 urls.append(f'http://127.0.0.1:5000/{i}') 11 12 #定義攜程函數-----簡單的基本架構 13 async def request(url): 14 async with aiohttp.ClientSession() as s: 15 #s.get/post和requests中的get/post用法幾乎同樣:url,headers,data/prames 16 #在s.get中若是使用代理操做:proxy="http://ip:port" 17 async with await s.get(url) as response: 18 #獲取字符串形式的響應數據:response.text() 19 #獲取byte類型的:response.read() 20 page_text = await response.text() 21 return page_text 22 23 #定義任務對象回調函數 24 def parse(task): 25 page_text = task.result() 26 print(f'響應結果:{page_text}') 27 28 29 #建立任務列表 30 start = time.time() 31 tasks = [] 32 for url in urls: 33 c = request(url) 34 task = asyncio.ensure_future(c) 35 task.add_done_callback(parse) 36 tasks.append(task) 37 #建立事件循環對象並將任務對象列表進行註冊 38 loop = asyncio.get_event_loop() 39 loop.run_until_complete(asyncio.wait(tasks)) 40 loop.close() 41 print(time.time()-start)#3.4196295738220215