На Python размещено большое количество HTTP-клиентов. Для тех из вас, кто не знаком с HTTP (протоколом передачи гипертекста), он является основой всей сети.
Сегодня мы сравним три самых популярных HTTP-клиента Python: Requests, HTTPX и AIOHTTP. Если вы хотите узнать о некоторых других доступных вариантах, посмотрите здесь.
Краткий обзор
Requests — это стандартный HTTP-клиент для Python. Он использует блокировку и синхронные операции для удобства использования. HTTPX — это новый асинхронный клиент, разработанный как для обеспечения как скорости, так и для простоты использования. AIOHTTP существует уже более десяти лет. Это один из первых и наиболее широко поддерживаемых асинхронных HTTP-клиентов Python.
Особенности | Requests | HTTPX | AIOHTTP |
---|---|---|---|
Структура | Синхронный/блокирующий | Асинхронный/неблокирующий | Асинхронный/неблокирующий |
Сеансы | Да | Да | Да |
Параллелизм | Нет | Да | Да |
Поддержка HTTP/2 | Нет | Да | Да |
Производительность | Низкая | Высокая | Высокая |
Повторы | Автоматические | Автоматические | Ручные |
Поддержка тайм-аута | По запросу | Полная поддержка | Полная поддержка |
Поддержка прокси-серверов | Да | Да | Да |
Простота использования | Простой | Сложный | Сложный |
Варианты использования | Простые проекты/прототипирование | Высокая производительность | Высокая производительность |
Python Requests
Python Requests очень интуитивно понятен и прост в использовании. Если вам не нужна высочайшая производительность, он лучше всего подходит для HTTP-запросов в Python. Он широко используется, прост в понимании и обладает лучшей в мире документацией среди HTTP-клиентов Python.
Вы можете установить его с помощью следующей команды.
Установка
pip install requests
Requests поддерживает стандартный протокол HTTP и даже некоторые функции управления сеансами. С помощью сеансов можно создать постоянное соединение с сервером. Это позволяет получать данные намного быстрее и эффективнее, чем при выполнении отдельных запросов. Если вы просто хотите выполнять простые запросы (GET, POST, PUT и DELETE) и вам не нужна высокая производительность, библиотека Requests может удовлетворить все ваши потребности в HTTP.
В Интернете вы можете найти руководства по интеграции прокси, пользовательских агентов и многому другому.
Ниже вы можете ознакомиться с некоторыми основными примерами использования.
import requests
# make a simple request
response = requests.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)
# use a session for multiple requests to the same server
with requests.Session() as client:
for get_request in range(1000):
response = client.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)
Клиента Requests будет недостаточно, когда речь заходит об асинхронных операциях. Благодаря поддержке асинхронного режима вы можете отправлять сразу несколько запросов. Затем вам нужно подождать
ответа на все из них. При синхронных запросах вы можете делать только один запрос за раз, и вам нужно дождаться ответа от сервера, прежде чем делать другой запрос. Если вашей программе нужно делать много HTTP-запросов, использование Requests привнесет некоторые ограничения в ваш код.
HTTPX
HTTPX — самая новая и современная из этих трех библиотек. Она дает нам полную поддержку асинхронных операций «из коробки». При этом она по-прежнему имеет удобный и интуитивно понятный синтаксис. Используйте HTTPX, если Requests просто не помогают и вам нужно повысить производительность без особых затрат на обучение.
С помощью asyncio
(асинхронный ввод и вывод) вы можете писать код, полностью использующий преимущества асинхронных ответов с помощью ключевого слова await
. Это позволяет нам продолжать наши операции, не блокируя все остальное в коде, пока мы ждем, пока что-то произойдет. С помощью этих асинхронных операций вы можете делать большие пакеты запросов. Вместо того, чтобы делать один запрос за раз, вы можете сделать 5, 50 или даже 100!
Установка
pip install httpx
Вот несколько примеров для начала работы с HTTPX.
import httpx
import asyncio
# synchronous response
response = httpx.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)
# basic async session usage
async def main():
async with httpx.AsyncClient() as client:
for get_request in range(1000):
response = await client.get("https://jsonplaceholder.typicode.com/posts")
print(response.status_code)
asyncio.run(main())
HTTPX — отличный выбор при написании нового кода, но у него есть свой набор ограничений. Из-за синтаксиса может быть сложно просмотреть и заменить существующую кодовую базу Requests клиентом HTTPX. Для написания асинхронного кода требуется шаблоны, и если вы не отправляете тысячи запросов, часто не стоит тратить дополнительное время на разработку.
По сравнению с AIOHTTP (как вы скоро узнаете), HTTPX работает не так быстро. Если вы хотите создать веб-сервер или сложную сеть, HTTPX — плохой выбор из-за неразвитой экосистемы. HTTPX лучше всего подходит для новых клиентских приложений с современными функциями, такими как HTTP/2.
AIOHTTP
Когда дело доходит до асинхронного программирования, AIOHTTP уже давно широко используется в Python. Независимо от того, применяете ли вы сервер, клиентское приложение или распределенную сеть, AIOHTTP может удовлетворить все эти потребности. Однако из трех клиентов (Requests, HTTPX и AIOHTTP) у AIOHTTP самое сложное обучение.
Мы сосредоточены на простом использовании на стороне клиента, поэтому не будем слишком углубляться в многочисленные нюансы AIOHTTP. Как и вслучае с HTTPX, мы можем делать пакеты запросов с помощью AIOHTTP. Однако, в отличие от HTTPX, AIOHTTP не поддерживает синхронные запросы. Только не делайте слишком много запросов за раз… вы же не хотите, чтобы вас забанил сервер.
Установка
pip install aiohttp
Ознакомьтесь с основами использования ниже.
import aiohttp
import asyncio
# make a single request
async def main():
async with aiohttp.ClientSession() as client:
response = await client.get("https://jsonplaceholder.typicode.com/posts")
print(response)
asyncio.run(main())
# basic async session usage
async def main():
with aiohttp.ClientSession() as client:
for response in range(1000):
response = await client.get("https://jsonplaceholder.typicode.com/posts")
print(response)
asyncio.run(main())
AIOHTTP работает строго асинхронно. Как видно из приведенного выше кода, в нашем примере с одним запросом требуется гораздо больше кода, чем в любом из первых двух примеров. Независимо от того, сколько запросов нам нужно сделать, нам нужно настроить асинхронный сеанс, поэтому рекомендуется комбинировать прокси с AIOHTTP. Для одного запроса это определенно излишне.
Несмотря на множество сильных сторон, AIOHTTP не сможет полностью заменить вам Python Requests, если вы не будете обрабатывать множество входящих и исходящих запросов одновременно. Тогда это значительно повысит вашу производительность. Используйте AIOHTTP при создании сложных приложений, требующих молниеносной связи. Эта библиотека лучше всего подходит для серверов, распределенных сетей и очень сложных приложений для веб-парсинга.
Сравнение производительности
Теперь мы создадим небольшую программу, используя каждую библиотеку. Требования просты: откройте сеанс клиента и выполните 1000 запросов API.
Сначала нам нужно создать сеанс с сервером. Далее мы выполняем 1000 запросов. У нас будет два массива: один для хороших ответов и один для плохих ответов. После завершения запуска мы выводим полное количество хороших и плохих запросов. Если мы получили неверные запросы, мы выводим их коды состояния на консоль.
В наших асинхронных примерах (HTTPX и AIOHTTP) мы будем использовать функцию chunkify()
. Она используется для разделения массива на части. Затем мы выполняем запросы в пакетном режиме. Например, если мы хотим выполнить запросы пакетами по 50 штук, мы создадим пакет с помощью chunkify()
, а затем используем process_chunk()
, чтобы выполнить все 50 запросов одновременно.
Взгляните на эти функции ниже.
def chunkify(iterable, size):
iterator = iter(iterable)
while chunk := list(islice(iterator, size)):
yield chunk
async def process_chunk(client, urls, retries=3):
tasks = [fetch(client, url, retries) for url in urls]
return await asyncio.gather(*tasks)
Requests
Вот наш код, использующий Requests. Он очень прост по сравнению с двумя асинхронными примерами, которые мы будем использовать позже. Мы открываем сеанс и используем цикл for
для итерации запросов.
import requests
import json
from datetime import datetime
start_time = datetime.now()
good_responses = []
bad_responses = []
with requests.Session() as client:
for get_request in range(1000):
response = client.get("https://jsonplaceholder.typicode.com/posts")
status_code = response.status_code
if status_code == 200:
good_responses.append(status_code)
else:
bad_responses.append(status_code)
end_time = datetime.now()
print("----------------Requests------------------")
print(f"Time elapsed: {end_time - start_time}")
print(f"Good Responses: {len(good_responses)}")
print(f"Bad Responses: {len(bad_responses)}")
for status_code in set(bad_responses):
print(status_code)
Requests выполнил нашу работу чуть более чем за 51 секунду. Это примерно 20 запросов в секунду. Без использования сеанса вы можете рассчитывать на 2 секунды на каждый запрос. Это довольно эффектно.
HTTPX
Вот код HTTPX. Мы используем chunkify()
и process_chunk()
, как мы упоминали ранее.
import httpx
import asyncio
from datetime import datetime
from itertools import islice
def chunkify(iterable, size):
iterator = iter(iterable)
while chunk := list(islice(iterator, size)):
yield chunk
async def fetch(client, url, retries=3):
"""Fetch a URL with retries."""
for attempt in range(retries):
try:
response = await client.get(url)
return response.status_code
except httpx.RequestError as e:
if attempt < retries - 1:
await asyncio.sleep(1)
else:
return f"Error: {e}"
async def process_chunk(client, urls, retries=3):
tasks = [fetch(client, url, retries) for url in urls]
return await asyncio.gather(*tasks)
async def main():
url = "https://jsonplaceholder.typicode.com/posts"
total_requests = 1000
chunk_size = 50
good_responses = []
bad_responses = []
async with httpx.AsyncClient(timeout=10) as client:
start_time = datetime.now()
urls = [url] * total_requests
for chunk in chunkify(urls, chunk_size):
results = await process_chunk(client, chunk)
for status in results:
if isinstance(status, int) and status == 200:
good_responses.append(status)
else:
bad_responses.append(status)
end_time = datetime.now()
print("----------------HTTPX------------------")
print(f"Time elapsed: {end_time - start_time}")
print(f"Good Responses: {len(good_responses)}")
print(f"Bad Responses: {len(bad_responses)}")
if bad_responses:
print("Bad Status Codes or Errors:")
for error in set(bad_responses):
print(error)
asyncio.run(main())
Вот наши результаты при использовании HTTPX. По сравнению с Requests это просто потрясающе. Общее время составило чуть более 7 секунд. Это 139,47 запросов в секунду. HTTPX обеспечил нам примерно в 7 раз большую производительность, чем Requests.
AIOHTTP
Теперь мы выполним то же упражнение, используя AIOHTTP. Мы используем ту же базовую структуру, что и в примере HTTPX. Единственное существенное отличие заключается в том, что клиент AIOHTTP заменяет клиент HTTPX.
import aiohttp
import asyncio
from datetime import datetime
from itertools import islice
def chunkify(iterable, size):
iterator = iter(iterable)
while chunk := list(islice(iterator, size)):
yield chunk
async def fetch(session, url, retries=3):
for attempt in range(retries):
try:
async with session.get(url) as response:
return response.status
except aiohttp.ClientError as e:
if attempt < retries - 1:
await asyncio.sleep(1)
else:
return f"Error: {e}"
async def process_chunk(session, urls):
tasks = [fetch(session, url) for url in urls]
return await asyncio.gather(*tasks)
async def main():
url = "https://jsonplaceholder.typicode.com/posts"
total_requests = 1000
chunk_size = 50
good_responses = []
bad_responses = []
async with aiohttp.ClientSession() as session:
start_time = datetime.now()
urls = [url] * total_requests
for chunk in chunkify(urls, chunk_size):
results = await process_chunk(session, chunk)
for status in results:
if isinstance(status, int) and status == 200:
good_responses.append(status)
else:
bad_responses.append(status)
end_time = datetime.now()
print("----------------AIOHTTP------------------")
print(f"Time elapsed: {end_time - start_time}")
print(f"Good Responses: {len(good_responses)}")
print(f"Bad Responses: {len(bad_responses)}")
if bad_responses:
print("Bad Status Codes or Errors:")
for error in set(bad_responses):
print(error)
asyncio.run(main())
AIOHTTP сработал молниеносно — чуть более 4 секунд. Этот HTTP-клиент обрабатывал более 241 запроса в секунду! AIOHTTP примерно в 10 раз быстрее Requests и почти на 50% быстрее HTTPX. В Python AIOHTTP занимает лидирующую позицию по производительности.
Как продукты Bright Data могут помочь
Bright Data предлагает ряд решений, которые могут улучшить рабочие процессы на основе HTTP-клиентов, особенно для операций с большим объемом данных, таких как веб-парсинг, запросы API и высокопроизводительная интеграция. Вот как вписывается каждый продукт:
- Резидентные прокси — резидентные прокси-серверы Bright Data помогают избежать блокировок и банов при веб-парсинге с использованием HTTP-клиентов Python, таких как AIOHTTP или HTTPX. Эти прокси-серверы имитируют поведение реальных пользователей, обеспечивая простой доступ к географически ограниченному или динамическому контенту.
- API Web Scraper. Вместо создания и обслуживания собственной инфраструктуры парсинга, API веб-парсера от Bright Data предоставляет получить предварительно настроенный доступ к сотням популярных веб-сайтов. Это позволяет сосредоточиться на анализе данных, а не на обработке запросов, повторных попыток или запретов. Просто используйте вызов API для прямого получения структурированных данных.
- Готовые наборы данных. Для тех, кому нужны конкретные элементы данных, но не хочется выполнять полный процесс парсинга, Bright Data предлагает готовые наборы данных, адаптированные к вашим потребностям. Эти наборы данных включают сведения о продуктах, цены и отзывы, и их можно мгновенно использовать для анализа электронной коммерции или исследования рынка.
- Web Unlocker. Web Unlocker автоматически обрабатывает такие задачи, как капчи, механизмы защиты от ботов и сложные шаблоны запросов. Соедините его с такими библиотеками, как HTTPX или AIOHTTP, чтобы упростить процесс парсинга труднодоступных веб-сайтов.
- SERP API. Ели вы извлекаете данные из поисковых систем, API SERP от Bright Data упрощает процесс, обеспечивая надежный доступ к результатам поиска, рекламе и рейтингам в реальном времени, не беспокоясь об инфраструктуре или блокировках.
Интегрируя инструменты Bright Data с HTTP-клиентами Python, вы можете создавать надежные и высокопроизводительные системы, упрощающие сбор данных и преодолевающие типичные проблемы, связанные с веб-парсингом и сбором данных.
Заключение
В мире HTTP-клиентов Requests являются стандартом просто из-за простоты использования. По сравнению с Requests, HTTPX больше похож на переход от конной повозки к современному автомобилю. Он обеспечит нам баланс между высокой производительностью и простотой использования. AIOHTTP похож на ракетный корабль. Вы не захотите использовать его без крайней необходимости, но это, безусловно, самый быстрый HTTP-клиент.
Подпишитесь на Bright Data сегодня, чтобы использовать мощные решения для обработки данных и получить конкурентное преимущество, необходимое вашему бизнесу. Все продукты поставляются с бесплатной пробной версией!
No credit card required