Написать пост

httpx vs. requests vs. aiohttp: кто лучше?

Разобрали на примере, какая из библиотек справляется лучше и как псинхронность влияет на скорость исполнения массовых запросов

httpx vs. requests vs. aiohttp: кто лучше?

С библиотекой requests питонисты знакомятся в первый же год, ведь на взаимодействии программ с веб-приложениями держится очень многое. HTTP-запросы позволяют общаться с API всевозможных сервисов, автоматизировать сбор данных с веб-страниц и в целом дают всевозможным системам взаимодействовать на расстоянии.

Если вы уже научились работать с re и захотели большего, статья поможет взглянуть поглубже на конкурентов, познакомиться с отличиями. Вы также узнаете, для какой задачи какой инструмент подойдет лучше.

Сравнительная таблица характеристик HTTP-клиентов:

httpx vs. requests vs. aiohttp: кто лучше? 1

Разница в синтаксисе

Базовый GET-запрос

Давайте сопоставим правила обращений для нашей тройки утилит. Так выглядит стандартный GET-запрос на requests:

			import requests
response = requests.get("https://example.com")
print(response.text)
		

А так — эквивалент на httpx:

			import httpx
response = httpx.get("https://example.com")
print(response.text)
		

В качестве альтернативы в httpx даже можно создать экземпляр HTTP-клиента:

			import httpx
client = httpx.Client()
response = httpx.get("https://example.com")
		

aiohttp написан специально для асинхронности, поэтому его стандартный код отправится в следующий раздел.

Поддержка одновременных запросов

Сниппет ниже — запрос на получение на aiohttp. Клиент задействует asyncio:

			import aiohttp
import asyncio

async def main(): 
    async with aiohttp.ClientSession() as session: 
            async with session.get("https://example.com") as response: 
                print(await response.text())

asyncio.run(main())
		

httpx тоже поддерживает параллельные вычисления:

			import httpx
import asyncio

async def main():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://example.com")
        print(response.text)

asyncio.run(main())
		

Код запроса на httpx/aiohttp немного длиннее из-за async/await. Однако это небольшая цена за возможность запускать составные процессы одновременно.

Поддержка http/2

Существует новая версия протокола HTTP под названием HTTP/2, которая обеспечивает более эффективную транспортировку данных и лучшую производительность. Если вы хотите узнать больше о нем, изучите статью. Из нашей троицы только httpx предлагает поддержку этого стандарта.

Чтобы использовать HTTP/2 с httpx, сначала установите дополнительный компонент:

			$ pip install 'httpx[http2]'
		

Затем задайте необязательному флагу http2 значение True при создании экземпляра клиента:

			client = httpx.Client(http2=True)
response = client.get("https://example.com")
print(response.http_version)
		

Сравним производительность

Давайте создадим простую программу, которая отправляет на веб-сайт конструктора ботов Aimylogic несколько запросов. Вот пример на requests:

			import time
import requests

start_time = time.perf_counter()

payload = {"filters": [{"key": "MESSAGE_TIME", "type": "DATE_TIME_RANGE", "from": "2023-09-25T00:00:00.000Z", "to": "2023-09-25T23:59:59.999Z"}]}

for i in range(20):
    r = requests.post(
    'https://app.jaicp.com/api/reporter/p//messages/filter', json=payload)

end_time = time.perf_counter()

print(f"requests: {end_time - start_time:.2f} сек.")
		

requests — синхронный инструмент, то есть будет исполнять запросы строго один за другим. Вот результаты запуска этого кода на Mac с 2,6 GHz 6‑ядерный процессор Intel Core i7 и интернетом (37 Мбит / сек):

			requests: 5.03 сек.
		

Если мы запустим аналогичный код на httpx и aiohttp, то блокировки предшествующим запросом не будет:

			import asyncio
import time
import httpx
import aiohttp

async def main():

    httpx_client = httpx.AsyncClient()
    aiohttp_client = aiohttp.ClientSession()

    try:
        # Отправим запросы с httpx
        start_time = time.perf_counter()
        tasks = [httpx_client.post("https://app.jaicp.com/api/reporter/p//messages/filter") for _ in range(20)]
        await asyncio.gather(*tasks)
        end_time = time.perf_counter()
        print(f"httpx: {end_time - start_time:.2f} seconds")

        # Отправим запросы с aiohttp
        start_time = time.perf_counter()
        tasks = [aiohttp_client.get("https://app.jaicp.com/api/reporter/p//messages/filter") for _ in range(20)]
        await asyncio.gather(*tasks)
        end_time = time.perf_counter()
        print(f"aiohttp: {end_time - start_time:.2f} seconds")
    finally:
        # Закроем сессии
        await aiohttp_client.close()
        await httpx_client.aclose()

asyncio.run(main())
		

Результат приятно удивляет, ведь скорость возросла в 45 и 55 раз для httpx и aiohttp соответственно:

			httpx: 0.11 seconds
aiohttp: 0.09 seconds
		

Еще одно отличие состоит в том, что httpx поддерживает async/sync-программирование, тогда как aiohttp является строго асинхронной библиотекой. Если вы хотите разобраться, в чем разница между этими понятиями, посмотрите это видео. Автор собирает две такие версии скриптов и создает с помощью нейронки NVidia лица несуществующих людей (thispersondoesnotexist.com).

Aiohttp отправляет запросы, и позволяет создавать HTTP-серверы.

httpx также автоматически декодирует JSON и другие распространенные форматы, что сэкономит время и усилия при работе со структурированными ответами.

Заключение

Цифры выше говорят сами за себя: с одновременными запросами стоит разбираться. re — хороший выбор, если вам нужна простая и удобная в использовании библиотека, или вы только приступили к теме запросов. Но когда требуется более многофункциональное решение с поддержкой асинхронности, httpx и aiohttp подойдут лучше.

А с какой альтернативой requests вы познакомились впервые? Поделитесь в комментариях.

Следите за новыми постами
Следите за новыми постами по любимым темам
5К открытий8К показов