異步請求庫aiohttp的使用
1.使用aiohttp發起一個請求
官方推薦使用一個客戶端會話來發起所有請求,會話中記錄了請求的cookie,但你還可以使用aiohttp.request來發送請求。
當我們使用 async def 就是定義了一個異步函數,異步邏輯由asyncio提供支持。
async with aiohttp.ClientSession() as session 為異步上下文管理器,在請求結束時或者發生異常請求時支持自動關閉實例化的客戶端會話。
import asyncio import aiohttp async def main(): async with aiohttp.ClientSession() as session: async with session.get('http://httpbin.org/get') as resp: print(resp.status) print(await resp.text()) if __name__ == '__main__': # python3.7才支持這種寫法,作為一個入口函數,以debug模式運行事件循環 asyncio.run(main(), debug=True) # python3.6及以下版本寫法 event_loop = asyncio.get_event_loop() results = event_loop.run_until_complete(asyncio.gather(main())) event_loop.close()
關於asyncio的一些關鍵字的說明:
- event_loop 事件循環:程序開啟一個無限循環,把一些函數注冊到事件循環上,當滿足事件發生的時候,調用相應的協程函數。
- coroutine 協程:協程對象,指一個使用async關鍵字定義的函數,它的調用不會立即執行函數,而是會返回一個協程對象。協程對象需要注冊到事件循環,由事件循環調用。
- task 任務:一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步封裝,其中包含了任務的各種狀態。
- future: 代表將來執行或沒有執行的任務的結果。它和task上沒有本質上的區別。
- async/await 關鍵字:python3.5用於定義協程的關鍵字,async定義一個協程,await用於掛起阻塞的異步調用接口。
2.發送post請求
除了常用的get和post請求還有以下幾種請求方式:
- put:
session.put('http://httpbin.org/put', data=b'data') - delete:
session.delete('http://httpbin.org/delete') - head:
session.head('http://httpbin.org/get') - options:
session.options('http://httpbin.org/get') - patch:
session.patch('http://httpbin.org/patch', data=b'data')
import asyncio import aiohttp async def main(): data = b'\x00Binary-data\x00' # 未經編碼的數據通過bytes數據上傳 data = 'text' # 傳遞文本數據 data = {'key': 'value'} # 傳遞form表單 async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', data=data) as resp: print(resp.status) print(await resp.text()) # 復雜的post請求 async def main2(): pyload = {'key': 'value'} # 傳遞pyload async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', json=pyload) as resp: print(resp.status) print(await resp.text()) if __name__ == '__main__': asyncio.run(main()) asyncio.run(main2())
3.向URL中傳遞參數
import asyncio import aiohttp async def main(): """以下三種方式均可以""" params = {'key1': 'value1', 'key2': 'value2'} params = [('key', 'value1'), ('key', 'value2')] params = 'key=value+1' async with aiohttp.ClientSession() as sess: async with sess.get('http://httpbin.org/get', params=params) as resp: print(resp.status) print(await resp.text()) if __name__ == '__main__': asyncio.run(main())
4.響應和狀態碼
由於獲取響應內容是一個阻塞耗時過程,所以我們使用await實現協程切換
- resp.history 查看重定向歷史
- resp.headers 獲取響應頭
- resp.cookies 獲取響應cookie
- resp.status 獲取響應狀態碼
- resp.text(encoding='utf-8) 獲取響應文本
- resp.json() 獲取json響應數據
- resp.read() 獲取二進制響應數據
- resp.content.read(100) 獲取流響應,每次獲取100個字節
import asyncio import aiohttp async def main(): params = {'key1': 'value1', 'key2': 'value2'} async with aiohttp.ClientSession() as sess: async with sess.get('http://httpbin.org/get', params=params) as resp: print(resp.status) # 狀態碼 print(await resp.text(encoding='utf-8')) # 文本響應,可以設置響應編碼,默認是utf-8 print(await resp.json()) # json響應 print(await resp.read()) # 二進制響應,適用於下載圖片等 async def main2(): async with aiohttp.ClientSession() as sess: async with sess.get('https://api.github.com/events') as resp: print(resp.status) # 狀態碼 print(await resp.content.read(100)) # 流響應,適用於大文件,分次讀取 # await生成一個迭代器,通過不斷循環拿到每一次100個字節碼 while True: chunk = await resp.content.read(100) print(chunk) if not chunk: break if __name__ == '__main__': asyncio.run(main()) asyncio.run(main2())
5.向目標服務器上傳文件
import asyncio import aiohttp async def main(): """傳遞文件""" files = {'file': open('report.xls', 'rb')} async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', data=files) as resp: print(resp.status) print(await resp.text()) async def main2(): """實例化FormData可以指定filename和content_type""" data = aiohttp.FormData() data.add_field('file', open('report.xls', 'rb'), filename='report.xls', content_type='application/vnd.ms-excel') async with aiohttp.ClientSession() as sess: async with sess.post('http://httpbin.org/post', data=data) as resp: print(resp.status) print(await resp.text()) async def main3(): """流式上傳文件""" async with aiohttp.ClientSession() as sess: with open('report.xls', 'rb') as f: async with sess.post('http://httpbin.org/post', data=f) as resp: print(resp.status) print(await resp.text()) async def main4(): """因為content屬性是 StreamReader(提供異步迭代器協議),所以您可以將get和post請求鏈接在一起。python3.6及以上才能使用""" async with aiohttp.ClientSession() as sess: async with sess.get('http://python.org') as resp: async with sess.post('http://httpbin.org/post', data=resp.content) as r: print(r.status) print(await r.text()) if __name__ == '__main__': asyncio.run(main()) asyncio.run