Requests, HTTPX o AIOHTTP: una comparación detallada

Explora Requests, HTTPX y AIOHTTP, los clientes HTTP más populares de Python, y encuentra el que mejor se adapte a tus necesidades de recopilación de datos.
13 min read
Requests vs. HTTPX vs. AIOHTTP blog image

Python aloja una gran variedad de clientes HTTP. Para quienes no conozcan el protocolo HTTP (protocolo de transferencia de hipertexto), se trata del marco subyacente de toda la web.

Hoy vamos a comparar tres de los clientes HTTP más populares de Python: Requests, HTTPX y AIOHTTP. Si quieres conocer algunos de los otros disponibles, echa un vistazo aquí.

Breve descripción

Requests es el cliente HTTP estándar para Python. Utiliza operaciones de bloqueo y síncronas para facilitar su uso. HTTPX es un cliente asíncrono más reciente diseñado para ofrecer velocidad y facilidad de uso. AIOHTTP existe desde hace más de una década. Es uno de los primeros y más compatibles clientes HTTP asíncronos que ofrece Python.

Función Requests HTTPX AIOHTTP
Estructura Síncrono/Con bloqueo Asíncrono/Sin bloqueo Asíncrono/Sin bloqueo
Sesiones
Simultaneidad No
Compatibilidad con HTTP/2 No
Rendimiento Bajo Alto Alto
Reintentos Automático Automático Manual
Compatibilidad con el tiempo de espera Por solicitud Compatibilidad completa Compatibilidad completa
Compatibilidad con proxy
Facilidad de uso Fácil Difícil Difícil
Casos prácticos Proyectos simples/Creación de prototipos Alto rendimiento Alto rendimiento

Python Requests

Python Requests es muy intuitivo y fácil de usar. Si no se requiere un rendimiento de vanguardia, es la opción ideal para realizar solicitudes HTTP en Python. Se usa ampliamente, es fácil de entender y es el cliente HTTP de Python mejor documentado del mundo.

Puedes instalarlo con el siguiente comando.

Instalación

pip install requests

Requests admite el protocolo HTTP estándar e incluso algunas funciones de gestión de sesiones. Con las sesiones, puedes crear una conexión persistente con un servidor. Esto te permite recuperar tus datos de manera mucho más rápida y eficiente al realizar solicitudes individuales. Si solo quieres hacer solicitudes básicas (GET, POST, PUT y DELETE) y el alto rendimiento no es un problema, la biblioteca de solicitudes puede satisfacer todas tus necesidades de HTTP.

En toda la web, puedes encontrar guías sobre integración de proxies, agentes de usuario y todo tipo de cosas.

Puedes ver algunos ejemplos de uso básicos a continuación.

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)

Las solicitudes se quedan cortas cuando se trata de operaciones asíncronas. Con la compatibilidad asíncrona, puedes hacer un lote de solicitudes a la vez. Luego, puedes esperar a todos. Con las solicitudes síncronas, solo se puede realizar una solicitud a la vez y hay que esperar a que el servidor reciba una respuesta antes de realizar otra solicitud. Si tu programa necesita hacer muchas solicitudes HTTP, el uso de Requests supondrá algunas limitaciones inherentes a tu código.

HTTPX

HTTPX es la más nueva y moderna de estas tres bibliotecas. Nos ofrece soporte completo para operaciones asíncronas desde el primer momento. No obstante, su sintaxis es intuitiva y fácil de usar. Usa HTTPX si Requests no es suficiente y necesitas mejorar el rendimiento sin una curva de aprendizaje excesiva.

Con asyncio (entrada y salida asíncronas), puedes escribir código que aproveche al máximo las respuestas asíncronas con la palabra clave await. Esto nos permite continuar con nuestras operaciones sin bloquear el resto del código mientras esperamos a que sucedan cosas. Con estas operaciones asíncronas, puedes realizar grandes lotes de solicitudes. En vez de hacer una solicitud cada vez, ¡puedes hacer 5, 50 o incluso 100!

Instalación

pip install httpx

He aquí algunos ejemplos para empezar a usar 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 es una opción excelente para escribir código nuevo, pero tiene su propio conjunto de limitaciones. Dada su sintaxis, puede resultar difícil revisar y reemplazar la base de código de Requests existente por HTTPX. Escribir código asíncrono requiere algo de texto repetitivo y, a menos que realices miles de solicitudes, no suele valer la pena el tiempo extra dedicado al desarrollo.

En comparación con AIOHTTP (como pronto descubrirás), HTTPX no es tan rápido. Si buscas construir un servidor web o una red compleja, HTTPX no es una buena opción debido a su ecosistema inmaduro. HTTPX es ideal para las aplicaciones nuevas del lado del cliente con funciones modernas como HTTP/2.

AIOHTTP

En cuanto a la programación asíncrona, AIOHTTP lleva mucho tiempo utilizándose en Python. Tanto si ejecutas un servidor, una aplicación cliente o una red distribuida, AIOHTTP puede satisfacer todas estas necesidades. Sin embargo, de las tres opciones (Requests, HTTPX y AIOHTTP), AIOHTTP tiene la curva de aprendizaje más pronunciada.

Nos centraremos en el uso sencillo del lado del cliente, por lo que no vamos a profundizar demasiado en la madriguera del conejo AIOHTTP. Al igual que HTTPX, podemos hacer lotes de solicitudes con AIOHTTP. Sin embargo, a diferencia de HTTPX, AIOHTTP no es compatible con solicitudes síncronas. En cualquier caso, no hagas demasiadas a la vez… pues el servidor podría banearte.

Instalación

pip install aiohttp

Echa un vistazo al uso básico a continuación.

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 es totalmente asíncrono. Como puedes ver en el código anterior, nuestro ejemplo de solicitud única requiere mucho más código que cualquiera de nuestros dos primeros ejemplos. No importa cuántas solicitudes tengamos que hacer, hay que configurar una sesión asíncrona, por lo que se recomienda combinar proxies con AIOHTTP. Para realizar una sola solicitud, sin duda, es exagerado.

Por muchos puntos fuertes que tenga, AIOHTTP no va a reemplazar completamente a Python Requests a menos que tengas que manejar montones de solicitudes entrantes y salientes a la vez. Así, aumentará notablemente tu rendimiento. Utiliza AIOHTTP cuando crees aplicaciones complejas y necesites una comunicación ultrarrápida. Esta biblioteca es la mejor para servidores, redes distribuidas y aplicaciones de extracción web de alta complejidad.

Comparación del rendimiento

Ahora, vamos a crear un programa pequeño usando cada biblioteca. Los requisitos son sencillos: abrir una sesión de cliente y realizar 1000 solicitudes de API.

En primer lugar, necesitamos crear nuestra sesión con el servidor. A continuación, realizamos 1000 solicitudes. Tendremos dos matrices: una para las respuestas exitosas y otra para las fallidas. Una vez finalizada la ejecución, imprimimos los recuentos completos de solicitudes exitosas y fallidas. Si recibimos solicitudes fallidas, imprimimos sus códigos de estado en la consola.

Con nuestros ejemplos asíncronos (HTTPX y AIOHTTP), utilizaremos una función chunkify(). Se usa para dividir una matriz en partes. A continuación, realizamos las solicitudes por lotes. Por ejemplo, si queremos realizar nuestras solicitudes en lotes de 50, usaremos chunkify() para crear el lote y usaremos process_chunk() para realizar las 50 solicitudes a la vez.

Echa un vistazo a estas funciones a continuación.

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

Este es nuestro código usando Requests. Es muy sencillo en comparación con los dos ejemplos asíncronos que usaremos más adelante. Abrimos una sesión y utilizamos un bucle for para iterar a través de las solicitudes.

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)
Resultados de las solicitudes en Python

Requests terminó el trabajo por nosotros en poco más de 51 segundos. Ello equivale a unas 20 solicitudes por segundo. Sin usar una sesión, puedes esperar 2 segundos por solicitud. Es bastante eficaz.

HTTPX

Este es el código de HTTPX. Utilizamos chunkify() y process_chunk() como mencionamos anteriormente.

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

Este es nuestro resultado al usar HTTPX. En comparación con las solicitudes, es alucinante. El tiempo total fue de poco más de 7 segundos. Ello equivale a 139,47 solicitudes por segundo. HTTPX nos ha proporcionado unas 7 veces el rendimiento de Requests.

Resultados de HTTPX

AIOHTTP

Ahora, realizaremos el mismo ejercicio con AIOHTTP. Seguimos la misma estructura básica que utilizamos en el ejemplo de HTTPX. La única diferencia importante aquí es cuando el cliente AIOHTTP reemplaza al cliente 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 llegó a la velocidad del rayo con poco más de 4 segundos. ¡Este cliente HTTP produjo más de 241 solicitudes por segundo! AIOHTTP es aproximadamente 10 veces más rápido que Requests y casi un 50 % más rápido que HTTPX. En Python, AIOHTTP está fuera de concurso en cuanto a rendimiento.

Resultados de AIOHTTP

Cómo pueden ayudar los productos de Bright Data

Bright Data ofrece una gama de soluciones que pueden mejorar tus flujos de trabajo basados en clientes HTTP, especialmente para operaciones con muchos datos, como el raspado web, las solicitudes de API y las integraciones de alto rendimiento. Así encaja cada producto:

  • Proxies residenciales: los proxies residenciales de Bright Data ayudan a evitar bloqueos y prohibiciones al raspar sitios web mediante clientes HTTP de Python como AIOHTTP o HTTPX. Estos proxies imitan con facilidad el comportamiento de los usuarios reales y brindan acceso a contenidos dinámicos o restringidos geográficamente.
  • API de Web Scraper: en vez de crear y mantener tu propia infraestructura de raspado, las API de Web Scraper de Bright Data brindan acceso preconfigurado a cientos de sitios web populares. Esto te permite centrarte en el análisis de datos en vez de gestionar las solicitudes, los reintentos o las prohibiciones. Solo tienes que utilizar una llamada a la API para obtener datos estructurados directamente.
  • Conjuntos de datos listos para usar: para quienes necesitan puntos de datos específicos, pero quieren evitar el raspado por completo, Bright Data ofrece conjuntos de datos listos para usar que se adaptan a tus necesidades. Estos conjuntos de datos incluyen detalles del producto, precios y reseñas y se pueden utilizar al instante para realizar análisis del comercio electrónico o investigaciones de mercado.
  • Web Unlocker: Web Unlocker gestiona automáticamente retos como los CAPTCHA, los mecanismos antibots y los patrones de solicitud complejos. Combínalo con bibliotecas como HTTPX o AIOHTTP para agilizar el proceso de raspado de sitios web de difícil acceso.
  • API SERP: si extraes datos de motores de búsqueda, la API SERP de Bright Data simplifica el proceso y ofrece un acceso fiable y en tiempo real a los resultados de búsqueda, los anuncios y las clasificaciones sin preocuparte por la infraestructura o el bloqueo.

Al integrar las herramientas de Bright Data con los clientes HTTP de Python, es posible crear sistemas robustos y de alto rendimiento que simplifican la recopilación de datos a la vez que superan los retos típicos del raspado web y la adquisición de datos.

Conclusión

En el mundo de los clientes HTTP, Requests es el estándar sencillamente por su facilidad de uso. En comparación con Requests, HTTPX es más como pasar de un carruaje de caballos a un coche moderno. Nos proporciona un equilibrio entre el alto rendimiento y la facilidad de uso. AIOHTTP es como un cohete espacial. No querrás usarlo a menos que sea absolutamente necesario, pero sigue siendo, con diferencia, el cliente HTTP más rápido que existe.

Regístrate en Bright Data hoy mismo para desbloquear potentes soluciones de datos y obtener la ventaja competitiva que tu empresa necesita. ¡Todos los productos vienen con una prueba gratuita!

No se requiere tarjeta de crédito