Внедрить душу в краулер aiohttp

Python рептилия

Студенты, которые слышали об асинхронных сканерах, должны были слышать о них более или менее.aiohttpэта библиотека. Он поставляется с Pythonasync/awaitРеализован асинхронный гусеничный гусеничный.

Используя aiohttp, мы можем написать краулер с параллелизмом, который конкурирует со Scrapy через API запросов.

Мы находимся в официальной документации aiohttp и видим, что она дает пример кода, как показано на следующем рисунке:

Теперь давайте немного изменим его, чтобы увидеть, насколько эффективно краулер написан таким образом.

Модифицированный код выглядит следующим образом:

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 страниц контента.

Вы можете посмотреть его в действии с видео ниже:

![](король тогда-1257411235.cos.AP-Чэнду. Нет денег cloud.com/slow.2019-1… 22_51_37.gif)

Можно сказать, что текущая скорость работы почти ничем не отличается от однопоточного краулера, написанного запросами, а кодов гораздо больше.

Итак, как правильно высвободить суперсилу aiohttp?

Теперь изменим код:

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 страниц контента, давайте посмотрим на видео ниже.

![](Король SO -1257411235.COS.AP-Чэнду. Нет денег cloud.com/fast.2019-1 ... 22_49_49.gif)

Видно, что эта скорость уже может быть коэффициентом скребковым. И всем нужно знать, что у этого краулера всего 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В сгенерированной асинхронной очереди каждый COROUTINE постоянно вытесняет URL-адреса от асинхронной очереди, в то время как истина и доступа к ним доступа до тех пор, пока асинхронная очередь пуста и выходит.

Когда программа запущена, Python автоматически запланирует эти 100 сопрограмм. Когда сопрограмма ожидает возврата сетевого ввода-вывода, она переключается на вторую сопрограмму и инициирует запрос. Когда эта сопрограмма ожидает возврата, она продолжает переключаться на третья сопрограмма, сопрограмма и инициировать запрос... . Программа полностью использует время ожидания сетевого ввода-вывода, что значительно повышает скорость работы.

Наконец, я хотел бы поблагодарить стажера Сяохэ за этот ускоренный план.