爲 aiohttp 爬蟲注入靈魂

爲 aiohttp 爬蟲注入靈魂

爲 aiohttp 爬蟲注入靈魂

攝影:產品經理
與產品經理在蘇州的小生活
據說過異步爬蟲的同窗,應該或多或少據說過aiohttp這個庫。它經過 Python 自帶的async/await實現了異步爬蟲。api

使用 aiohttp,咱們能夠經過 requests 的api寫出併發量匹敵 Scrapy 的爬蟲。網絡

咱們在 aiohttp 的官方文檔上面,能夠看到它給出了一個代碼示例,以下圖所示:
爲 aiohttp 爬蟲注入靈魂session

咱們如今稍稍修改一下,來看看這樣寫爬蟲,運行效率如何。併發

修改之後的代碼以下:app

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())

這段代碼訪問個人爬蟲練習站100次,獲取100頁的內容。異步

你們能夠經過下面這個視頻看看它的運行效率:
爲 aiohttp 爬蟲注入靈魂async

能夠說,目前這個運行速度,跟 requests 寫的單線程爬蟲幾乎沒有區別,代碼還多了那麼多。ide

那麼,應該如何正確釋放 aiohttp 的超能力呢?oop

咱們如今把代碼作一下修改:url

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())

在修改之後的代碼裏面,我讓這個爬蟲爬1000頁的內容,咱們來看看下面這個視頻。
爲 aiohttp 爬蟲注入靈魂

能夠看到,目前這個速度已經能夠跟 Scrapy 比一比了。而且你們須要知道,這個爬蟲只有1個進程1個線程,它是經過異步的方式達到這個速度的。

那麼,修改之後的代碼,爲何速度能快那麼多呢?

關鍵的代碼,就在:

tasks = []
for _ in range(100):
    task = get(session, queue)
    tasks.append(task)
await asyncio.wait(tasks)

在慢速版本里面,咱們只有1個協程在運行。而在如今這個快速版本里面,咱們建立了100個協程,並把它提交給asyncio.wait來統一調度。asyncio.wait會在全部協程所有結束的時候才返回。

咱們把1000個 URL 放在asyncio.Queue生成的一個異步隊列裏面,每個協程都經過 while True 不停從這個異步隊列裏面取 URL 並進行訪問,直到異步隊列爲空,退出。

當程序運行時,Python 會自動調度這100個協程,當一個協程在等待網絡 IO 返回時,切換到第二個協程併發起請求,在這個協程等待返回時,繼續切換到第三個協程併發起請求……。程序充分利用了網絡 IO 的等待時間,從而大大提升了運行速度。

最後,感謝實習生小河給出的這種加速方案。

爲 aiohttp 爬蟲注入靈魂

相關文章
相關標籤/搜索