Los mejores clientes HTTP de Python para el raspado web

Descubre los principales clientes HTTP de Python, cuáles son sus funciones y cuáles son los mejores casos prácticos para el raspado web en 2024.
17 min read
Best Python HTTP Clients blog image

Los clientes HTTP son bibliotecas útiles de Python que permiten que tu código envíe solicitudes a servidores web o a las API y que reciban respuestas. Facilitan el envío de varios tipos de solicitudes HTTP (GET, POST, PUT, DELETE, etc.), la recopilación de datos, el envío de datos o la realización de acciones en sitios web o en las API.

En cuanto al raspado web, estos clientes se suelen usar junto con bibliotecas de análisis HTML como Beautiful Soup o html5lib.

En este artículo, tendrás la oportunidad de conocer algunos de los mejores clientes HTTP de Python, incluidos Requests, urllib3, Uplink, GRequests, HTTPX y aiohttp. Analizarás cada uno de ellos en función de sus características, de su facilidad de uso, de su documentación y asistencia técnica, e incluso de su popularidad. Al final de este artículo, tendrás una idea clara de qué biblioteca es la que encaja mejor con tu caso práctico.

Requests

Vamos a empezar por el cliente HTTP de Python más conocido: la biblioteca Requests, con la impresionante cantidad de 30 millones de descargas por semana.

Este es un ejemplo de cómo puedes gestionar las solicitudes HTTP y las respuestas con Requests:

import requests

print("Testing `requests` library...")
resp = requests.get('https://httpbin.org/get', params={"foo": "bar"})
if resp.status_code == 200:     # success
    print(f"Response Text: {resp.text} (HTTP-{resp.status_code})")
else:   # error
    print(f"Error: HTTP-{resp.status_code}")

Ten en cuenta que httpbin.org ofrece ejemplos de respuestas para probar distintos métodos HTTP.

En este fragmento de código, el método requests.get(...) toma la URL que se busca y un único parámetro de consulta, foo. Puedes pasar cualquier parámetro de consulta con el parámetro params

Con Requests, así como con otros clientes HTTP, no hace falta añadir manualmente las cadenas de consulta a las URL ni codificar los datos; la biblioteca se encarga de hacerlo por ti. Para enviar datos en formato JSON, pasas un diccionario de Python con el parámetro data y puedes recibir la respuesta en formato JSON directamente con resp.json().

La biblioteca de Requests gestiona automáticamente los reenvíos HTTP (3xx) de forma predeterminada, lo cual es muy útil en las tareas de raspado web para acceder al contenido de las URL redirigidas. También admite conexiones SSL (capa de puertos seguros).

Si se examina bajo lupa, Requests utilizada urllib3 para gestionar las tareas HTTP de bajo nivel, como la agrupación de conexiones y la verificación SSL, haciendo que ofrezca una API más propia de Python y de un nivel más alto para los desarrolladores.

Además, Requests es compatible con la gestión de sesiones, lo que te permite conservar parámetros en varias solicitudes, como cookies, encabezados o identificadores. Esto es útil, sobre todo, para las tareas de raspado web en las que es fundamental conservar estos parámetros para acceder a contenido restringido.

La biblioteca de Requests también admite las transmisiones, una función muy práctica para las tareas de extracción de datos web que implican grandes respuestas, como la descarga de archivos o el procesamiento de datos de transmisión desde las API.

Para procesar los datos de respuesta de manera eficiente sin cargarlos todos en la memoria, puedes usar métodos como iter_content() o iter_lines():

import requests

print("Testing `requests` library with streaming...")
resp = requests.get('https://httpbin.org/stream/10', stream=True)
for chunk in resp.iter_content(chunk_size=1024):
    print(chunk.decode('utf-8'))

No obstante, la biblioteca Requests no cuenta con funciones asincrónicas integradas ni con un soporte de almacenamiento en caché integrado (aunque está disponible a través de requests-cache). Además, Requests no admite HTTP/2 y es poco probable que se añada pronto, tal y como se cuenta en esta conversación.

Nota: HTTP/2 es la versión más reciente del protocolo HTTP y se ha diseñado para que sea más rápida y más eficiente que HTTP/1.1. Permite que se produzcan múltiples solicitudes y respuestas por medio de una única conexión de TCP (protocolo de control de transmisión) mediante la multiplexación, lo que reduce las conexiones entre el cliente y el servidor y ayuda a que las páginas carguen con mayor rapidez. Sin embargo, la compatibilidad con HTTP/2 sigue estando limitada.

La biblioteca Requests simplifica las interacciones HTTP de Python. Destaca por sus métodos simples, por su sintaxis concisa, por su agrupación automática de las conexiones para una mayor eficiencia y por su decodificación integrada de archivos en formato JSON. Gracias a su facilidad de uso, a su gestión de las sesiones, a sus funciones de transmisión y a su amplia documentación, Requests es una opción muy popular entre los desarrolladores.

urllib3

urllib3 es una biblioteca examinada en detalle y muy utilizada para hacer solicitudes HTTP, no solo por parte de los desarrolladores, sino también por muchos otros clientes HTTP. Ofrece funciones útiles y opciones de personalización para gestionar solicitudes HTTP de bajo nivel cuando se hacen raspados web.

Este es un ejemplo básico en el que se hace una solicitud HTTP utilizando urllib3:

import urllib3

print("Testing `urllib3` library...")
http = urllib3.PoolManager()    # PoolManager for connection pooling
resp = http.request('GET', 'https://httpbin.org/get', fields={"foo": "bar"})

if resp.status == 200:     # success
    print(f"Response: {resp.data.decode('utf-8')} (HTTP-{resp.status})")
else:    # error
    print(f"Error: HTTP-{resp.status}")

En este fragmento de código, urllib3.poolManager() crea un conjunto de conexiones que se pueden reutilizar para varias solicitudes, lo que mejora el rendimiento porque evita la sobrecarga de tener que establecer nuevas conexiones para cada solicitud. Junto con la URL, puede pasar los parámetros de consulta necesarios mediante su parámetro fields

Una característica que merece la pena destacar de urllib3 es su capacidad para gestionar la transmisión de respuestas, lo que te permite procesar una gran cantidad de datos de forma eficiente sin tener que cargarlos todos en la memoria. Esta función es muy útil para descargar archivos de gran tamaño o para consumir varias API de transmisión durante la extracción de datos web.

urllib3 es compatible con los reenvíos automáticos de forma predeterminada y cuenta con soporte SSL. Sin embargo, no dispone de funciones asincrónicas integradas, soporte de almacenamiento en caché ni administración de sesiones (como las cookies), y no admite HTTP/2.

Aunque la gestión de la agrupación de conexiones de urllib3 es menos fácil de usar que la de la biblioteca Requests, urllib3 usa una sintaxis de secuencias de comandos sencilla, lo que la diferencia de algunos clientes que necesitan disponer de un enfoque basado en clases o en decoradores, y esto hace que urllib3 sea útil para las interacciones HTTP básicas. Además, urllib3 viene con una documentación muy cuidada.

Si estás buscando una biblioteca potente y no necesitas gestionar distintas sesiones, urllib3 es ideal para tareas sencillas de raspado web.

Uplink

Uplink es un cliente HTTP de Python potente, pero menos conocido. Simplifica las interacciones con las API RESTful mediante interfaces basadas en clases y esto hace que sea especialmente útil para el raspado web que implica llamadas a la API.

Echa un vistazo a este código de ejemplo en el que se usa Uplink para llamar a un extremo de la API:

import uplink

@uplink.json
class JSONPlaceholderAPI(uplink.Consumer):
    @uplink.get("/posts/{post_id}")
    def get_post(self, post_id):
        pass


def demo_uplink():
    print("Testing `uplink` library...")
    api = JSONPlaceholderAPI(base_url="https://jsonplaceholder.typicode.com")
    resp = api.get_post(post_id=1)
    if resp.status_code == 200:     # success
        print(f"Response: {resp.json()} (HTTP-{resp.status_code})")
    else:   # error
        print(f"Error:HTTP-{resp.status_code}")

Este fragmento de código define una clase JsonPlaceholderApi que hereda de Uplink.consumer. Utiliza el decorador @uplink.get para crear una solicitud HTTP GET para que la API JSONPlaceholder recupere una publicación específica. El parámetro post_id se incluye de forma dinámica en el extremo con este decorador: @uplink.get(«/posts/{post_id}»). El sitio https://jsonplaceholder.typicode.com simula una API REST y ofrece respuestas en formato JSON para las pruebas y para el desarrollo.

Uplink es compatible con SSL y gestiona los reenvíos de forma automática para obtener una respuesta final. También ofrece funciones avanzadas como Bring Your Own HTTP Library.

Sin embargo, Uplink no tiene soporte integrado para la transmisión de respuestas, solicitudes asincrónicas, almacenamiento en caché (aunque puede usar requests-cache) ni HTTP/2.

Uplink ofrece una documentación adecuada por sus potentes funciones, pero no se actualiza muy a menudo (la última versión fue la 0.9.7 en marzo de 2022) y no es muy popular. Aunque su enfoque basado en clases y su sintaxis de decorador pueden llamar la atención de desarrolladores orientados a objetos, su facilidad de uso solo es moderada para quienes prefieren el estilo de programación de Python.

Los usuarios suelen elegir Uplink cuando necesitan recopilar datos, principalmente, de varios extremos de API RESTful y no de páginas HTML.

GRequests

GRequests es una extensión de la conocida biblioteca Requests que es compatible con las solicitudes asincrónicas. Permite obtener datos de manera simultánea desde varios sitios web o desde varias API.

A diferencia de las solicitudes sucesivas, que esperan cada respuesta antes de enviar la siguiente, GRequests ofrece una mejor eficiencia porque envía varias solicitudes al mismo tiempo. Esto viene bien, sobre todo, para obtener datos de varios sitios web o de varias API.

Echa un vistazo a este ejemplo:

import grequests

print("Testing `grequests` library...")
# Fetching data from multiple URLs
urls = [
    'https://www.python.org/',
    'http://httpbin.org/get',
    'http://httpbin.org/ip',
]

responses = grequests.map((grequests.get(url) for url in urls))
for resp in responses:
    print(f"Response for: {resp.url} ==> HTTP-{resp.status_code}")

En este código, GRequests envía a la vez tres solicitudes GET a diferentes URL usando grequests.map(...) y reúne las respuestas en una lista llamada responses. Después, repite estas respuestas para emitirlas. GRequests usa gevent de forma interna, una biblioteca de redes basada en corrutinas pensadas para solicitudes HTTP asincrónicas. Gracias a esta función, puedes enviar varias solicitudes HTTP al mismo tiempo sin tener que preocuparte de que la gestión sea compleja. Un caso práctico de este ejemplo podría ser extraer noticias de diferentes sitios web para un tema o para una categoría en particular.

GRequests también cuenta con funciones como la gestión automática de reenvíos, la compatibilidad con SSL y el procesamiento de transmisiones de respuestas sin cargarlas todas en la memoria a la vez. Sin embargo, ten en cuenta que GRequests no es compatible con HTTP/2 ni ofrece funciones integradas de almacenamiento en caché, aunque puede usar requests-cache.

GRequests simplifica las solicitudes HTTP asincrónicas con métodos intuitivos similares a los de su biblioteca base, Requests. Acaba con la necesidad de estructuras complejas async/await para encargarse de la simultaneidad, lo que facilita su uso. No obstante, su documentación es mínima porque su base de código es pequeña (213 líneas en su versión 0.7.0) y porque su actividad de desarrollo es reducida. Estos factores hacen que sea un cliente HTTP menos popular.

GRequests es una opción que tener en cuenta por sus funciones asincrónicas fáciles de usar, ideal para cuando necesites recopilar datos de varias fuentes al mismo tiempo.

HTTPX

HTTPX es un cliente HTTP moderno que cuenta con múltiples funciones para Python y que se utiliza mucho para todo tipo de proyectos de raspado web. Está diseñado para reemplazar la biblioteca Requests de Python y, al mismo tiempo, ofrecer soporte asincrónico y un mejor rendimiento.

El siguiente ejemplo muestra una solicitud HTTP GET asincrónica con HTTPX:

import httpx
import asyncio

async def fetch_posts():
    async with httpx.AsyncClient() as client:
        response = await client.get('https://jsonplaceholder.typicode.com/posts')
        return response.json()

async def httpx_demo():
    print("Testing `httpx` library...")
    posts = await fetch_posts()
    for idx, post in enumerate(posts):
        print(f"Post #{idx+1}: {post['title']}")

# async entry point to execute the code
asyncio.run(httpx_demo())

Este código define una función asincrónica llamada fetch_posts(), que recupera entradas de blog ficticias de la API https://jsonplaceholder.typicode.com/posts mediante httpx.asyncClient(). Otra función asincrónica, httpx_demo(), espera a que fetch_posts() devuelva esas publicaciones y, a continuación, emite sus títulos en bucle. Por último, asyncio.run(httpx_demo()) sirve como punto de entrada para ejecutar httpx_demo() de forma asincrónica.

Además de su soporte integrado para clientes HTTP asincrónicos, HTTPX también ofrece soporte HTTP/2 integrado. Esto permite cargar varios recursos al mismo tiempo con mayor rapidez a través de una sola conexión TCP, lo que dificulta que los sitios web rastreen la huella digital del navegador cuando se está haciendo la extracción de datos web.

Para enviar una solicitud HTTP/2, solo tienes que definir el parámetro HTTP2=true al crear un cliente HTTPX:

import httpx

client = httpx.Client(http2=True)
response = client.get("https://http2.github.io/")
print(response)

Ten en cuenta que, para usar HTTP/2, necesitas instalar HTTPX con su soporte para http2

pip install httpx[http2]

Además, HTTPX ofrece un excelente soporte para la transmisión de respuestas, por lo que puedes gestionar de forma eficiente respuestas o flujos de datos de gran tamaño sin cargar la respuesta completa en la memoria.

Este es un ejemplo de transmisión de respuesta de texto mediante HTTPX:

with httpx.stream("GET", "https://httpbin.org/stream/10") as resp:
   for text in resp.iter_text():
       print(text)

Aunque HTTPX no incluye funciones de almacenamiento en caché integradas, puedes usar Hishel en su lugar.

HTTPX no sigue los reenvíos HTTP de forma predeterminada, pero puedes habilitar esta función con el parámetro follow_redirects

import httpx

# test http --> https redirect
response = httpx.get('http://github.com/', follow_redirects=True)

Aunque sus funciones asincrónicas presentan cierta complejidad, HTTPX ofrece métodos sencillos para la comunicación HTTP y es compatible con solicitudes sincrónicas que son fáciles de usar. Esto hace que sea accesible tanto para desarrolladores principiantes como expertos. Además, cada vez se está usando más gracias a su amplia documentación y a una comunidad activa de desarrolladores que crean herramientas para la integración de HTTPX.

Si está buscando un cliente HTTP asincrónico y que cuente con muchas funciones, HTTPX es muy buena opción.

aiohttp

Al igual que HTTPX, aiohttp ofrece soporte asincrónico integrado para solicitudes HTTP. Sin embargo, aiohttp está diseñado exclusivamente para la programación asincrónica, lo que le permite destacar en situaciones que requieren solicitudes simultáneas y sin bloqueos. Esto hace que sea ideal para proyectos de raspado web de alto rendimiento y es fácil de usar con proxies.

Así es como puedes usar aiohttp para el raspado web de varias URL al mismo tiempo:

import asyncio
import aiohttp

async def fetch_data(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()    

async def demo_aiohttp():
    print("Testing `aiohttp` library...")
    urls = [
        'https://www.python.org/',
        'http://httpbin.org/get',
        'http://httpbin.org/ip',
    ]
    tasks = [fetch_data(url) for url in urls]
    responses = await asyncio.gather(*tasks)
    for resp_text in responses:
        print(f"Response: {resp_text}")

# async entry point to execute the code      
asyncio.run(demo_aiohttp())

La función asincrónica fetch_data(...) crea un AIOHttp.clientSession() y envía una solicitud GET a la URL que se ha especificado. Después, crea una lista de tareas para cada URL y las ejecuta al mismo tiempo con asyncio.gather(...). Una vez terminadas todas las tareas, se recopilan y se emiten los datos extraídos (en este caso, el texto de la respuesta). La ejecución real se inicia con asyncio.run(demo_aiohttp()).

aiohttp gestiona de forma automática los reenvíos HTTP y es compatible con la transmisión de respuestas, lo que garantiza una gestión eficiente de archivos o de flujos de datos de gran tamaño sin que haya que recurrir a un uso excesivo de la memoria. También es muy flexible y ofrece una amplia gama de extensiones y de middleware de terceros.

Además, aiohttp también se puede usar como servidor de desarrollo en caso necesario, aunque este artículo se centra únicamente en la funcionalidad de su cliente HTTP.

Sin embargo, aiohttp no cuenta con soporte para HTTP/2 ni con funciones de almacenamiento en caché integradas. Aun así, puede integrar el almacenamiento en caché mediante bibliotecas como aiohttp-client-cache cuando haga falta.

aiohttp puede ser más difícil de usar que los clientes HTTP más simples, como Requests, sobre todo para los usuarios que sean principiantes. Su naturaleza asincrónica y sus funciones adicionales hacen que sea necesario tener unos buenos conocimientos de programación asincrónica en Python. No obstante, es bastante popular y tiene 14 700 estrellas en GitHub y numerosas bibliotecas de terceros creadas sobre aiohttp, que también ofrece una documentación muy completa para los desarrolladores.

Si buscas tener un soporte asincrónico completo, aiohttp es una buena opción. Su rendimiento asincrónico hace que sea ideal para tareas de extracción de datos en tiempo real, como hacer seguimiento de los precios de las acciones o rastrear acontecimientos en directo a medida que se desarrollan, como las elecciones.

Consulta la siguiente tabla para obtener una descripción general rápida de los principales clientes HTTP de Python:

Requests urllib3 Uplink GRequests HTTPX aiohttp
Facilidad de uso Fácil De fácil a moderado Moderado Fácil Moderado Moderado
Reenvíos automáticos Hay que activarlos
Soporte SSL
Capacidad asincrónica No No No
Transmisión de respuestas No
Soporte HTTP/2 No No No No No
Soporte de almacenamiento en caché Vía: requests-cache No Vía: requests-cache Vía: requests-cache Vía: Hishel Vía: aiohttp-client-cache

Conclusión

En este artículo, has aprendido todo lo necesario sobre algunos clientes HTTP de Python conocidos, como Requests, urllib3, Uplink, GRequests, HTTPX y aiohttp, cada uno con características únicas como su sencillez, su compatibilidad asincrónica, sus transmisiones y su soporte HTTP/2.

Aunque Requests, Uplink y GRequests son conocidos por ser fáciles de usar, aiohttp y HTTPX ofrecen funciones asincrónicas muy potentes. Requests sigue siendo el más conocido, pero aiohttp y HTTPX están ganando terreno por sus funciones asincrónicas. En definitiva, tendrás que analizar cada una de ellas para elegir la que mejor se adapte a tus necesidades.

En el caso de raspado web en la vida real, tendrás que tener en cuenta algo más que tu cliente HTTP, como saltarte las medidas antibots y usar proxies. Por suerte, Bright Data te puede ayudar.

Bright Data facilita el raspado web con herramientas como el IDE de Web Scraper, que ofrece funciones y plantillas de JavaScript listas para usar, y la herramienta Web Unlocker, que evita los CAPTCHA y las medidas antibots. La herramienta Scraping Browser de Bright Data se integra con Puppeteer, Playwright y Selenium para recopilar datos en varios pasos. Además, las redes proxy y los servicios de Bright Data permiten el acceso desde distintas ubicaciones. Estas herramientas se encargan de tareas complejas, como la gestión de proxies y la resolución de los CAPTCHA, para que puedas centrarte en obtener los datos que necesitas.

Empieza tu prueba gratuita hoy mismo y disfruta de todo lo que tiene que ofrecerte Bright Data.

No se requiere tarjeta de crédito