Python編程異步爬蟲之a(chǎn)iohttp模塊的基本用法
異步爬蟲—aiohttp的使用
基本介紹
asyncio模塊其內(nèi)部實現(xiàn)了對TCP、UDP、SSL協(xié)議的異步操作,但是對于HTTP請求來說,就需要用aiohttp實現(xiàn)了。
aiohttp是一個基于asyncio的異步HTTP網(wǎng)絡(luò)模塊,它既提供了服務(wù)端,又提供了客戶端。requests發(fā)起的是同步網(wǎng)絡(luò)請求,aiohttp則是異步。 aiohttp
模塊是一個基于 asyncio 的 HTTP 客戶端和服務(wù)器框架,可以用于異步處理 HTTP 請求和響應(yīng)。以下是一個簡單的示例代碼展示 aiohttp
模塊的基本用法:
首先,你需要安裝 aiohttp
模塊:
pip install aiohttp
基本示例
接下來,以下是一個簡單的示例代碼,演示了如何使用 aiohttp
發(fā)送一個簡單的 GET 請求并獲取響應(yīng):
import aiohttp import asyncio async def fetch(session, url): async with session.get(url) as response: return await response.text(), response.status async def main(): async with aiohttp.ClientSession() as session: html, status = await fetch(session, 'https://cuiqingcai.com') print(f'html:{html[:100]}....') print(f'status: {status}') if __name__ == '__main__': loop = asyncio.get_event_loop() loop.run_until_complete(main()) 運行結(jié)果如下: html:<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content.... status: 200
在這個示例中,我們定義了一個 fetch
函數(shù)來發(fā)送 GET 請求并返回響應(yīng)文本,然后使用 main
函數(shù)調(diào)用 fetch
函數(shù)。最后,創(chuàng)建事件循環(huán)并運行主函數(shù)。
由于網(wǎng)頁源碼過長,這里只截取了輸出的一部分,可以看到,成功獲取了網(wǎng)頁的源代碼及響應(yīng)狀態(tài)碼200,成功使用aiohttp通過異步方式完成了網(wǎng)頁爬取。當然,這個操作用requests也可以做到。
URL參數(shù)設(shè)置
對于URL參數(shù)的設(shè)置,可以借助params參數(shù),傳入一個字典即可,示例如下:
import aiohttp import asyncio async def main(): params = {'name': 'germey', 'age':25} async with aiohttp.ClientSession() as session: async with session.get('https://www.httpbin.org/get', params=params) as response: print(await response.text()) if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main()) 運行結(jié)果如下: { "args": { "age": "25", "name": "germey" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.8.3", "X-Amzn-Trace-Id": "Root=1-65ffa6c9-6d501fa57391d9fc1d7b5f28" }, "origin": "58.21.224.175", "url": "https://www.httpbin.org/get?name=germey&age=25" }
這里看到,實際請求的URL為https://www.httpbin.org/get?name=germey&age=25,其中的參數(shù)對應(yīng)于params的內(nèi)容。
其他請求類型
aiohttp還支持其他請求類型,如POST、PUT、DELETE等,這和requests的使用方式有點類似,示例如下:
session.post('http://www.httpbin.org/post', data=b'data') session.put('http://www.httpbin.org/put', data=b'data') session.delete('http://www.httpbin.org/delete') session.head('http://www.httpbin.org/get') session.options('http://www.httpbin.org/get') session.patch('http://www.httpbin.org/patch', data=b'data')
POST請求
當使用 aiohttp
模塊時,除了發(fā)送簡單的 GET 請求之外,你還可以處理 POST 請求、設(shè)置請求頭、處理異常等。以下是一個稍微復(fù)雜一點的示例代碼,展示了如何發(fā)送 POST 請求并處理異常:
import aiohttp import asyncio async def main(): data = {'name': 'germey', 'age':25} async with aiohttp.ClientSession() as session: async with session.post('https://www.httpbin.org/post', data=data) as response: print(await response.text()) if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main()) 運行結(jié)果如下: { "args": {}, "data": "", "files": {}, "form": { "age": "25", "name": "germey" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "18", "Content-Type": "application/x-www-form-urlencoded", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.8.3", "X-Amzn-Trace-Id": "Root=1-65ffabc5-3bbf3cdb7cb413a3475ece45" }, "json": null, "origin": "58.21.224.175", "url": "https://www.httpbin.org/post" }
在上面的示例中,我們定義了一個 fetch
函數(shù)來發(fā)送一個包含 JSON 數(shù)據(jù)的 POST 請求,并在發(fā)生異常時進行處理。在 main
函數(shù)中調(diào)用 fetch
函數(shù),并根據(jù)返回的狀態(tài)碼和響應(yīng)文本輸出相應(yīng)信息。
通過這個示例,你可以進一步了解如何使用 aiohttp
模塊處理 POST 請求和異常情況。你可以根據(jù)自己的需求進行進一步的定制和擴展。希望這能幫助你更深入地理解 aiohttp
模塊的用法。
對于POST J SON數(shù)據(jù)提交,其對應(yīng)的請求頭中的Content-Type為application/json,我們只需將post方法里的data參數(shù)改成json即可,實例代碼如下:
import json import aiohttp import asyncio async def main(): data = {'name': 'germey', 'age': 25} async with aiohttp.ClientSession() as session: async with session.post('https://www.httpbin.org/post', json=data) as response: print(await response.text()) if __name__ == '__main__': print(asyncio.get_event_loop().run_until_complete(main())) 運行結(jié)果如下: { "args": {}, "data": "{\"name\": \"germey\", \"age\": 25}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "29", "Content-Type": "application/json", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.8.4", "X-Amzn-Trace-Id": "Root=1-65ffc207-7d035c6f2b342778797881c9" }, "json": { "age": 25, "name": "germey" }, "origin": "58.21.224.175", "url": "https://www.httpbin.org/post" }
響應(yīng)
我們可以用如下方法分別獲取其中的狀態(tài)碼、響應(yīng)頭、響應(yīng)體、響應(yīng)體二進制內(nèi)容、響應(yīng)體JSON結(jié)果,實例代碼如下:
import aiohttp import asyncio async def main(): data = {'name': 'germey', 'age': 25} async with aiohttp.ClientSession() as session: async with session.post('https://www.httpbin.org/post', data=data) as response: print('status:', response.status) print('headers:', response.headers) print('body:', await response.text()) print('bytes:', await response.read()) print('json:', await response.json()) if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main()) 運行結(jié)果如下: status: 200 headers: <CIMultiDictProxy('Date': 'Sun, 24 Mar 2024 06:34:54 GMT', 'Content-Type': 'application/json', 'Content-Length': '510', 'Connection': 'keep-alive', 'Server': 'gunicorn/19.9.0', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true')> body: { "args": {}, "data": "", "files": {}, "form": { "age": "25", "name": "germey" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "18", "Content-Type": "application/x-www-form-urlencoded", "Host": "www.httpbin.org", "User-Agent": "Python/3.9 aiohttp/3.8.4", "X-Amzn-Trace-Id": "Root=1-65ffc98e-153aad5f0e536d773cd978b4" }, "json": null, "origin": "58.21.224.175", "url": "https://www.httpbin.org/post" } bytes: b'{\n "args": {}, \n "data": "", \n "files": {}, \n "form": {\n "age": "25", \n "name": "germey"\n }, \n "headers": {\n "Accept": "*/*", \n "Accept-Encoding": "gzip, deflate", \n "Content-Length": "18", \n "Content-Type": "application/x-www-form-urlencoded", \n "Host": "www.httpbin.org", \n "User-Agent": "Python/3.9 aiohttp/3.8.4", \n "X-Amzn-Trace-Id": "Root=1-65ffc98e-153aad5f0e536d773cd978b4"\n }, \n "json": null, \n "origin": "58.21.224.175", \n "url": "https://www.httpbin.org/post"\n}\n' json: {'args': {}, 'data': '', 'files': {}, 'form': {'age': '25', 'name': 'germey'}, 'headers': {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Content-Length': '18', 'Content-Type': 'application/x-www-form-urlencoded', 'Host': 'www.httpbin.org', 'User-Agent': 'Python/3.9 aiohttp/3.8.4', 'X-Amzn-Trace-Id': 'Root=1-65ffc98e-153aad5f0e536d773cd978b4'}, 'json': None, 'origin': '58.21.224.175', 'url': 'https://www.httpbin.org/post'}
可以看到,有些字段前需要加await,有些則不需要。其原則是,如果返回一個協(xié)程對象(如async修飾的方法),那么前面就要加await,具體可以看aiohttp的API,鏈接為https://docs.aiohttp.org/en/stable/client_reference.html。
超時設(shè)置
我們可以借助ClientTimeout對象設(shè)置超時,例如要設(shè)置1秒的超時時間,可以這么實現(xiàn):
import asyncio import aiohttp async def main(): timeout = aiohttp.ClientTimeout(total=2) async with aiohttp.ClientSession(timeout=timeout) as session: async with session.get('https://www.httpbin.org/get') as response: print('status:', response.status) if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main()) 運行結(jié)果如下: 200
并發(fā)限制
由于aiohttp可以支持非常高的并發(fā)量,目標網(wǎng)站可能無法在短時間內(nèi)響應(yīng),而且有瞬間將目標網(wǎng)站爬掛掉的危險。
一般情況下,可以借助asyncio的Semaphore來控制并發(fā)量,示例代碼如下:
import asyncio import aiohttp CONCURRENCY = 5 URL = 'https://www.baidu.com' semaphore = asyncio.Semaphore(CONCURRENCY) # session = None async def scrape_api(): async with semaphore: print('scraping',URL) async with session.get(URL) as response: await asyncio.sleep(1) return await response.text() async def main(): global session session = aiohttp.ClientSession() scrape_index_tasks = [asyncio.ensure_future(scrape_api()) for _ in range(100000)] await asyncio.gather(*scrape_index_tasks) if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(main())\ 運行結(jié)果: scraping https://www.baidu.com scraping https://www.baidu.com scraping https://www.baidu.com scraping https://www.baidu.com scraping https://www.baidu.com ....
這里我們聲明CONCURRENCY(代表爬取的最大并發(fā)量)為5, 同時聲明爬取的目標URL為百度。接著,借助Semaphore創(chuàng)建了一個信號量對象,將其賦值為semaphore,這樣就可以用它來控制最大并發(fā)量了。這里我們把semaphore直接放置在了對應(yīng)的爬取方法里,使用async with語句將semaphore作為上下文對象即可。信號量便可以控制進入爬取的最大協(xié)程數(shù)量,即我們聲明的CONCURRENCY的值。在main方法里, 我們聲明10 000個task,將其傳遞給gather方法運行,倘若不加以限制,那這10000個task會被同時執(zhí)行,并發(fā)數(shù)量相當大。有了信號量的控制后,同時運行的task數(shù)量最大會被控制5個,這樣就能給aiohttp限制速度了。
到此這篇關(guān)于Python編程異步爬蟲之a(chǎn)iohttp模塊的基本用法的文章就介紹到這了,更多相關(guān)Python aiohttp使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python實現(xiàn)視頻讀取和轉(zhuǎn)化圖片
今天小編就為大家分享一篇python實現(xiàn)視頻讀取和轉(zhuǎn)化圖片,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12Django 多表關(guān)聯(lián) 存儲 使用方法詳解 ManyToManyField save
今天小編就為大家分享一篇Django 多表關(guān)聯(lián) 存儲 使用方法詳解 ManyToManyField save,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08Python 獲取windows桌面路徑的5種方法小結(jié)
今天小編就為大家分享一篇Python 獲取windows桌面路徑的5種方法小結(jié),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07Python創(chuàng)建文件和追加文件內(nèi)容實例
這篇文章主要介紹了Python創(chuàng)建文件和追加文件內(nèi)容實例,本文同時給出了把標準輸出定向到文件實例,需要的朋友可以參考下2014-10-10python+splinter實現(xiàn)12306網(wǎng)站刷票并自動購票流程
這篇文章主要為大家詳細介紹了python+splinter實現(xiàn)12306網(wǎng)站刷票并自動購票流程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09Python實現(xiàn)圖片轉(zhuǎn)字符畫的示例
本篇文章主要介紹了Python實現(xiàn)圖片轉(zhuǎn)字符畫的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08Python基礎(chǔ)語法之變量與數(shù)據(jù)類型詳解
這篇文章主要為大家詳細介紹了Python基礎(chǔ)語法中變量與數(shù)據(jù)類型的用法,文中的示例代碼講解詳細,對我們學(xué)習(xí)Python有一定的幫助,感興趣的可以了解一下2022-07-07