Python異步爬蟲實現(xiàn)原理與知識總結(jié)
一、背景
默認情況下,用get請求時,會出現(xiàn)阻塞,需要很多時間來等待,對于有很多請求url時,速度就很慢。因為需要一個url請求的完成,才能讓下一個url繼續(xù)訪問。一種很自然的想法就是用異步機制來提高爬蟲速度。通過構(gòu)建線程池或者進程池完成異步爬蟲,即使用多線程或者多進程來處理多個請求(在別的進程或者線程阻塞時)。
import time #串形 def getPage(url): print("開始爬取網(wǎng)站",url) time.sleep(2)#阻塞 print("爬取完成?。。?,url) urls = ['url1','url2','url3','url4','url5'] beginTime = time.time()#開始計時 for url in urls: getPage(url) endTime= time.time()#結(jié)束計時 print("完成時間%d"%(endTime - beginTime))
下面通過模擬爬取網(wǎng)站來完成對多線程,多進程,協(xié)程的理解。
二、多線程實現(xiàn)
import time #使用線程池對象 from multiprocessing.dummy import Pool def getPage(url): print("開始爬取網(wǎng)站",url) time.sleep(2)#阻塞 print("爬取完成?。?!",url) urls = ['url1','url2','url3','url4','url5'] beginTime = time.time()#開始計時 #準備開啟5個線程,并示例化對象 pool = Pool(5) pool.map(getPage, urls)#urls是可迭代對象,里面每個參數(shù)都會給getPage方法處理 endTime= time.time()#結(jié)束計時 print("完成時間%d"%(endTime - beginTime))
完成時間只需要2s!!!!!!!!
線程池使用原則:適合處理耗時并且阻塞的操作
三、協(xié)程實現(xiàn)
單線程+異步協(xié)程?。。。。。。。。。娏彝扑],目前流行的方式。
相關(guān)概念:
#%% import time #使用協(xié)程 import asyncio async def getPage(url): #定義了一個協(xié)程對象,python中函數(shù)也是對象 print("開始爬取網(wǎng)站",url) time.sleep(2)#阻塞 print("爬取完成?。?!",url) #async修飾的函數(shù)返回的對象 c = getPage(11) #創(chuàng)建事件對象 loop_event = asyncio.get_event_loop() #注冊并啟動looP loop_event.run_until_complete(c) #task對象使用,封裝協(xié)程對象c ''' loop_event = asyncio.get_event_loop() task = loop_event.create_task(c) loop_event.run_until_complete(task) ''' #Future對象使用,封裝協(xié)程對象c 用法和task差不多 ''' loop_event = asyncio.get_event_loop() task = asyncio.ensure_future(c) loop_event.run_until_complete(task) ''' #綁定回調(diào)使用 async def getPage2(url): #定義了一個協(xié)程對象,python中函數(shù)也是對象 print("開始爬取網(wǎng)站",url) time.sleep(2)#阻塞 print("爬取完成?。。?,url) return url #async修飾的函數(shù)返回的對象 c2 = getPage2(2) def callback_func(task): print(task.result()) #task.result()返回任務(wù)對象中封裝的協(xié)程對象對應(yīng)函數(shù)的返回值 #綁定回調(diào) loop_event = asyncio.get_event_loop() task = asyncio.ensure_future(c2) task.add_done_callback(callback_func) #真正綁定, loop_event.run_until_complete(task)
輸出:
四、多任務(wù)協(xié)程實現(xiàn)
import time #使用多任務(wù)協(xié)程 import asyncio urls = ['url1','url2','url3','url4','url5'] async def getPage(url): #定義了一個協(xié)程對象,python中函數(shù)也是對象 print("開始爬取網(wǎng)站",url) #在異步協(xié)程中如果出現(xiàn)同步模塊相關(guān)的代碼,那么無法實現(xiàn)異步 #time.sleep(2)#阻塞 await asyncio.sleep(2)#遇到阻塞操作必須手動掛起 print("爬取完成?。?!",url) return url beginTime = time.time() #任務(wù)列表,有多個任務(wù) tasks = [] for url in urls: c = getPage(url) task = asyncio.ensure_future(c)#創(chuàng)建任務(wù)對象 tasks.append(task) loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks))#不能直接放task,需要封裝進入asyncio,wait()方法中 endTime = time.time() print("完成時間%d"%(endTime - beginTime))
此時不能用time.sleep(2),用了還是10秒
對于真正爬取過程中,如在getPage()方法中真正爬取數(shù)據(jù)時,即requests.get(url) ,它是基于同步方式實現(xiàn)。應(yīng)該使用異步網(wǎng)絡(luò)請求模塊aiohttp
參考下面代碼:
async def getPage(url): #定義了一個協(xié)程對象,python中函數(shù)也是對象 print("開始爬取網(wǎng)站",url) #在異步協(xié)程中如果出現(xiàn)同步模塊相關(guān)的代碼,那么無法實現(xiàn)異步 #requests.get(url)#阻塞 async with aiohttp.ClintSession() as session: async with await session.get(url) as response: #手動掛起 page_text = await response.text() #.text()返回字符串,read()返回二進制數(shù)據(jù),注意不是content print("爬取完成?。?!",url) return page_text
到此這篇關(guān)于Python異步爬蟲實現(xiàn)原理與知識總結(jié)的文章就介紹到這了,更多相關(guān)Python異步爬蟲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python函數(shù)缺省值與引用學(xué)習筆記分享
有關(guān)一個在函數(shù)參數(shù)設(shè)置缺省值與引用的問題,這個問題是大多數(shù)Pythoner可能會忽視的問題,作個筆記,以備后閱,同時供需要的朋友參考2013-02-02關(guān)于Python函數(shù)對象的名稱空間和作用域
這篇文章主要介紹了關(guān)于Python函數(shù)對象的名稱空間和作用域,數(shù)據(jù)的名稱是儲存到棧區(qū),而數(shù)據(jù)的內(nèi)容是儲存到堆區(qū),當我們要去使用數(shù)據(jù)的內(nèi)容時,我們可以通過數(shù)據(jù)的名稱來直接去表示數(shù)據(jù)的內(nèi)容,需要的朋友可以參考下2023-04-04python 實現(xiàn)Requests發(fā)送帶cookies的請求
這篇文章主要介紹了python 實現(xiàn)Requests發(fā)送帶cookies請求的方法,幫助大家更好的理解和使用python,感興趣的朋友可以了解下2021-02-02python3環(huán)境搭建過程(利用Anaconda+pycharm)完整版
這篇文章主要介紹了python3環(huán)境搭建過程(利用Anaconda+pycharm)完整版,本文給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08