讓你的爬蟲速度提高100倍,就用這招!

聽(tīng)說(shuō)過(guò)異步爬蟲的同學(xué),應(yīng)該或多或少聽(tīng)說(shuō)過(guò)aiohttp這個(gè)庫(kù)。它通過(guò) Python 自帶的async/await實(shí)現(xiàn)了異步爬蟲。
使用 aiohttp,我們可以通過(guò) requests 的api寫出并發(fā)量匹敵 Scrapy 的爬蟲。
我們?cè)?aiohttp 的官方文檔上面,可以看到它給出了一個(gè)代碼示例,如下圖所示:

我們現(xiàn)在稍稍修改一下,來(lái)看看這樣寫爬蟲,運(yùn)行效率如何。
修改以后的代碼如下:
import asyncio
import aiohttp
template = 'http://exercise.kingname.info/exercise_middleware_ip/{page}'
async def get(session, page):
url = template.format(page=page)
resp = await session.get(url)
print(await resp.text(encoding='utf-8'))
async def main():
async with aiohttp.ClientSession() as session:
for page in range(100):
await get(session, page)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
這段代碼訪問(wèn)我的爬蟲練習(xí)站100次,獲取100頁(yè)的內(nèi)容。
大家可以通過(guò)下面這個(gè)視頻看看它的運(yùn)行效率:

可以說(shuō),目前這個(gè)運(yùn)行速度,跟 requests 寫的單線程爬蟲幾乎沒(méi)有區(qū)別,代碼還多了那么多。
那么,應(yīng)該如何正確釋放 aiohttp 的超能力呢?
我們現(xiàn)在把代碼做一下修改:
import asyncio
import aiohttp
template = 'http://exercise.kingname.info/exercise_middleware_ip/{page}'
async def get(session, queue):
while True:
try:
page = queue.get_nowait()
except asyncio.QueueEmpty:
return
url = template.format(page=page)
resp = await session.get(url)
print(await resp.text(encoding='utf-8'))
async def main():
async with aiohttp.ClientSession() as session:
queue = asyncio.Queue()
for page in range(1000):
queue.put_nowait(page)
tasks = []
for _ in range(100):
task = get(session, queue)
tasks.append(task)
await asyncio.wait(tasks)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
在修改以后的代碼里面,我讓這個(gè)爬蟲爬1000頁(yè)的內(nèi)容,我們來(lái)看看下面這個(gè)視頻。

可以看到,目前這個(gè)速度已經(jīng)可以跟 Scrapy 比一比了。并且大家需要知道,這個(gè)爬蟲只有1個(gè)進(jìn)程1個(gè)線程,它是通過(guò)異步的方式達(dá)到這個(gè)速度的。
那么,修改以后的代碼,為什么速度能快那么多呢?
關(guān)鍵的代碼,就在:
tasks = []
for _ in range(100):
task = get(session, queue)
tasks.append(task)
await asyncio.wait(tasks)
在慢速版本里面,我們只有1個(gè)協(xié)程在運(yùn)行。而在現(xiàn)在這個(gè)快速版本里面,我們創(chuàng)建了100個(gè)協(xié)程,并把它提交給asyncio.wait來(lái)統(tǒng)一調(diào)度。asyncio.wait會(huì)在所有協(xié)程全部結(jié)束的時(shí)候才返回。
我們把1000個(gè) URL 放在asyncio.Queue生成的一個(gè)異步隊(duì)列里面,每一個(gè)協(xié)程都通過(guò) while True 不停從這個(gè)異步隊(duì)列里面取 URL 并進(jìn)行訪問(wèn),直到異步隊(duì)列為空,退出。
當(dāng)程序運(yùn)行時(shí),Python 會(huì)自動(dòng)調(diào)度這100個(gè)協(xié)程,當(dāng)一個(gè)協(xié)程在等待網(wǎng)絡(luò) IO 返回時(shí),切換到第二個(gè)協(xié)程并發(fā)起請(qǐng)求,在這個(gè)協(xié)程等待返回時(shí),繼續(xù)切換到第三個(gè)協(xié)程并發(fā)起請(qǐng)求……。程序充分利用了網(wǎng)絡(luò) IO 的等待時(shí)間,從而大大提高了運(yùn)行速度。
推薦閱讀:
入門: 最全的零基礎(chǔ)學(xué)Python的問(wèn)題 | 零基礎(chǔ)學(xué)了8個(gè)月的Python | 實(shí)戰(zhàn)項(xiàng)目 |學(xué)Python就是這條捷徑
量化: 定投基金到底能賺多少錢? | 我用Python對(duì)去年800只基金的數(shù)據(jù)分析
干貨:爬取豆瓣短評(píng),電影《后來(lái)的我們》 | 38年NBA最佳球員分析 | 從萬(wàn)眾期待到口碑撲街!唐探3令人失望 | 笑看新倚天屠龍記 | 燈謎答題王 |用Python做個(gè)海量小姐姐素描圖 |碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影
趣味:彈球游戲 | 九宮格 | 漂亮的花 | 兩百行Python《天天酷跑》游戲!
AI: 會(huì)做詩(shī)的機(jī)器人 | 給圖片上色 | 預(yù)測(cè)收入 | 碟中諜這么火,我用機(jī)器學(xué)習(xí)做個(gè)迷你推薦系統(tǒng)電影
小工具: Pdf轉(zhuǎn)Word,輕松搞定表格和水印! | 一鍵把html網(wǎng)頁(yè)保存為pdf!| 再見(jiàn)PDF提取收費(fèi)! | 用90行代碼打造最強(qiáng)PDF轉(zhuǎn)換器,word、PPT、excel、markdown、html一鍵轉(zhuǎn)換 | 制作一款釘釘?shù)蛢r(jià)機(jī)票提示器! |60行代碼做了一個(gè)語(yǔ)音壁紙切換器天天看小姐姐!|
年度爆款文案
點(diǎn)閱讀原文,領(lǐng)AI全套!


