Volver al blog
Guías
Mihnea-Octavian Manolache2 de diciembre de 202213 min de lectura

Los 3 mejores clientes HTTP de Python para el scraping web

Los 3 mejores clientes HTTP de Python para el scraping web

¿Qué son los clientes HTTP de Python y cómo se utilizan?

Para comprender mejor cómo funciona Internet, conviene familiarizarse con el Protocolo de Transferencia de Hipertexto (HTTP). Sin embargo, hoy nos centraremos principalmente en los clientes HTTP de Python. Por lo tanto, daré por hecho que ya estás familiarizado con el HTTP.

En términos generales, un cliente HTTP es una instancia o un programa que facilita la comunicación con un servidor. Por ejemplo, un navegador web puede considerarse un cliente HTTP. Sin embargo, como programadores, rara vez utilizamos un navegador real al desarrollar una aplicación, salvo cuando trabajamos en un rastreador web o cuando estamos investigando.

Dicho esto, cuando hablamos de clientes HTTP desde un punto de vista más programático, normalmente nos referimos a un método o a una instancia de una clase que se utiliza para ejecutar solicitudes HTTP. Dado que Python es, sin duda, uno de los lenguajes de programación más populares (y también mi favorito), hoy hablaremos de los mejores clientes HTTP para Python y de cómo implementarlos en un proyecto real.

Comprender el protocolo HTTP

Antes de continuar, aunque recomiendo consultar la documentación sobre HTTP, permíteme repasar rápidamente algunos de los conceptos básicos de HTTP. En primer lugar, HTTP es quizá uno de los protocolos de Internet más utilizados. Lo usamos a diario para intercambiar información entre clientes y servidores. 

Para que esto sea posible, HTTP utiliza métodos de solicitud. Estos métodos indican la acción que un cliente desea realizar en un servidor. Por ejemplo, si quieres obtener información de un servidor, utilizarías GET. Si quieres enviar algo al servidor, utilizarías POST. A continuación se muestra una lista de los métodos de solicitud HTTP más comunes:

  • GET: recuperar datos del servidor
  • HEAD: solo recupera el encabezado, sin el cuerpo (los datos propiamente dichos)
  • POST: envía información al servidor
  • PUT: envía información al servidor y sustituye todas las representaciones actuales del recurso
  • PATCH: envía información al servidor y modifica parcialmente el recurso
  • ELIMINAR: elimina el recurso del servidor

¿Por qué Python para las solicitudes HTTP?

En primer lugar, Python tiene una sintaxis estupenda y una comunidad aún mejor. Por eso, es perfecto para aprender. Yo mismo, cuando empecé a programar, elegí Python. De hecho, los clientes HTTP de Python fueron de las primeras tecnologías con las que entré en contacto. Pero eso ya es otro tema. 

Mi objetivo con el artículo de hoy es asegurarme de que al terminar no solo tengas unos conocimientos teóricos básicos, sino también una visión general de su aplicación práctica. 

Y Python es ideal para ambas cosas por varias razones. Por citar solo algunas:

  • Sintaxis: escribir en Python se parece mucho a escribir en español. Por eso, leer un script en Python te ayudará a relacionar los conceptos teóricos con su aplicación práctica. 
  • Asistencia: Python cuenta con una comunidad muy amplia. La mayoría de las veces, si te quedas atascado, basta con hacer una simple pregunta en StackOverflow para encontrar la solución a tu problema. 
  • Disponibilidad: la biblioteca de paquetes de Python es una de las más amplias. Por ejemplo, solo en lo que respecta a los clientes HTTP de Python, hay más de una docena de paquetes. Pero hoy nos centraremos en los más populares. 

3 (+1) Los mejores clientes HTTP para Python

A la hora de clasificar los paquetes para elaborar una lista con los tres mejores clientes HTTP para Python, creo que es una cuestión tanto de funcionalidad como de preferencias personales. Por lo tanto, es correcto decir que lo que sigue representa mi selección de las tres mejores bibliotecas de clientes HTTP para Python, más que una clasificación general.

1. Solicitudes: simplicidad eficaz

Requests es probablemente uno de los clientes HTTP más populares en la comunidad de Python. Yo no soy una excepción. Cada vez que pruebo un nuevo rastreador web, utilizo Python con Requests. Es tan sencillo como escribir .get y tan potente como un navegador web real. 

Entre otras cosas, la biblioteca de solicitudes ofrece:

  • Verificación SSL 
  • Compatibilidad con servidores proxy para HTTPS
  • Persistencia de las cookies y sesiones
  • Función de mantenimiento de conexión
  • Autenticación personalizada

Y estos son solo algunos ejemplos. Puedes consultar la lista completa de funciones aquí. Ahora te voy a enseñar cómo trabajar con las solicitudes:

import requests
r = requests.get("http://google.com")      
print(r.test)

Como puedes ver, con solo tres líneas de código, la biblioteca requests nos ayuda a recuperar el código HTML de una fila desde un servidor. En el ejemplo anterior, estamos realizando una solicitud GET al servidor y mostrando el resultado. Pero, como ya he dicho, esta biblioteca ofrece muchas más posibilidades. Creemos un ejemplo más complejo que utilice funciones como los proxies y las solicitudes POST:

import requests

def get_params(object):
    params = ''
    for key,value in object.items():
        if list(object).index(key) < len(object) - 1:
            params += f"{key}={value}."
        else:
            params += f"{key}={value}"
    return params

API_KEY = '<YOUR_API_KEY>'

TARGET_URL = 'https://httpbin.org/post'

DATA = {"foo":"bar"}

PARAMETERS = {
    "proxy_type":"datacenter",
    "device":"desktop"
}

PROXY = {
    "http": f"http://webscrapingapi.{ get_params(PARAMETERS) }:{ API_KEY }@proxy.webscrapingapi.com:80",
    "https": f"https://webscrapingapi.{ get_params(PARAMETERS) }:{ API_KEY }@proxy.webscrapingapi.com:8000"
}

response = requests.post(
    url=TARGET_URL,
    data=DATA,
    proxies=PROXY,
    verify=False
)

print(response.text)

Veamos qué estamos haciendo aquí:

  • Estamos definiendo la función `get_params`, que toma un objeto y lo devuelve como una cadena de parámetros de URL.
  • Vamos a definir nuestras variables: undefinedundefinedundefinedundefinedundefined
  • Estamos utilizando el método `post` de Requests para enviar una solicitud HTTP POST.
  • Estamos imprimiendo el cuerpo de la respuesta

2. HTTPX: las solicitudes reinventadas

HTTPX es relativamente nuevo en el panorama. Sin embargo, en muy poco tiempo se ha convertido en uno de los clientes HTTP para Python más recomendados. Por ejemplo, Flask (uno de los principales marcos de trabajo web para Python) recomienda el uso de HTTPX en su documentación oficial. 

Cuando dije que HTTPX es una versión renovada de requests, me refería a que ambas bibliotecas tienen una sintaxis muy similar. De hecho, HTTPX busca ser totalmente compatible con requests. Solo hay unas pocas diferencias de diseño menores entre ambas, que se destacan aquí

Así es como se ve una solicitud POST básica en HTTPX:

import httpx

TARGET_URL = 'https://httpbin.org/post'

DATA = {"foo":"bar"}

r = httpx.post(
   url=TARGET_URL,
   data=DATA,
)

print(r.text)

Como puedes ver, lo que cambiamos principalmente es el nombre del paquete, en comparación con el ejemplo de Requests. Y dado que son tan similares, la pregunta sigue siendo: ¿por qué elegir HTTPX en lugar de Requests? Bueno, para empezar, HTTPX es uno de los pocos clientes HTTP de Python que ofrece compatibilidad asíncrona. En resumen, HTTPX es una excelente opción si quieres refactorizar tu código basado en Requests. 

3. urllib3: conexiones seguras para subprocesos

Python cuenta con varios «urllib», lo que suele confundir a los programadores noveles. La principal diferencia entre urllib, urllib2 y urllib3 radica en las características de cada paquete. urllib fue el cliente HTTP original de Python, incluido en la biblioteca estándar de Python 1.2. urllib2 fue la versión mejorada, introducida en Python 1.6 y destinada a sustituir al urllib original.

Sin embargo, en lo que respecta a urllib3, se trata en realidad de un cliente HTTP de Python de terceros. A pesar de su nombre, esta biblioteca no tiene nada que ver con sus dos «predecesoras». Además, en la comunidad de Python se rumorea que no hay intención de incluir urllib3 en la biblioteca estándar. Al menos en un futuro próximo. 

Aunque este paquete no está vinculado oficialmente a la biblioteca estándar de Python, muchos desarrolladores lo utilizan porque ofrece:

  • Seguridad entre subprocesos
  • Verificación SSL/TLS del lado del cliente
  • Compatibilidad con servidores proxy HTTP y SOCKS
  • Cobertura completa de las pruebas

Ahora que ya hemos visto la parte teórica, veamos un ejemplo de aplicación:

import urllib3,json

TARGET_URL = 'https://httpbin.org/post'

DATA = {"foo":"bar"}

http = urllib3.PoolManager()

encoded_data = json.dumps(DATA)

r = http.request('POST', TARGET_URL, body=encoded_data)   

print(r.data.decode('utf-8'))

Analicemos las diferencias detectadas entre urllib3 y Requests:

  • `http`: una instancia del método `PoolManager`, que se encarga de los detalles relacionados con la seguridad de los subprocesos y la agrupación de conexiones
  • `encoded_data`: una cadena JSON convertida que contiene la carga útil que vamos a enviar
  • `r`: la solicitud POST que estamos realizando con la ayuda de urllib3. En este caso, estamos utilizando el método `request` de la instancia `PoolManager`.

Y, por último, tenemos que descodificar los datos que recibimos en respuesta a nuestra solicitud. Como puedes ver, hay un par de cosas que hacemos de forma diferente a como lo hacíamos con Requests.

Mención de honor: http.client - Cliente HTTP tradicional de Python

http.client también forma parte de la biblioteca estándar de Python. Por lo general, los programadores no lo utilizan directamente. Por ejemplo, urllib lo utiliza como dependencia para gestionar las solicitudes HTTP y HTTPS. Lo he incluido en nuestra clasificación porque creo que, como programadores, es bueno conocer la «estructura básica» de los paquetes que utilizamos.

Así que, aunque quizá no llegues a crear un proyecto real con http.client, aquí tienes un ejemplo de implementación que, estoy seguro, te ayudará a comprender mejor cómo funcionan los clientes HTTP en Python:

import http.client

TARGET_URL = 'www.httpbin.org'

http = http.client.HTTPSConnection(TARGET_URL)
http.request("GET", "/get")

r = http.getresponse()

print(r.read().decode('utf-8'))

La instancia `HTTPSConnection` admite varios parámetros, que puedes consultar aquí. En nuestro ejemplo, solo definimos el `método` y la `URL` (o, más exactamente, el punto final). Además, al igual que urllib3, http.client devuelve una respuesta codificada. Por lo tanto, debemos descodificarla antes de mostrarla en pantalla.

Caso práctico: Creación de un rastreador con Requests

Ahora que ya sabemos cómo utilizar los clientes HTTP, vamos a proponernos un pequeño proyecto. Te servirá no solo para poner en práctica lo que has aprendido, sino también para enriquecer tu propio portfolio de programación. 

Dado que los clientes HTTP de Python se utilizan habitualmente para recopilar información de los servidores, el uso más común de estas tecnologías es crear un rastreador web. Así que, de aquí en adelante, nos centraremos en cómo crear un rastreador web utilizando clientes HTTP en Python. Como tengo un favorito personal —requests—, lo utilizaré para este proyecto. Sin embargo, puedes utilizarlo como punto de partida e incluso modificarlo para usar algunas de las otras tecnologías que hemos comentado. Sin más preámbulos, empecemos a programar:

1. Configuración del proyecto

Empecemos creando un nuevo directorio en el que guardaremos los archivos de nuestro rastreador web. Ahora abre una nueva ventana de terminal y accede a ese directorio con el comando `cd`. Aquí queremos crear un nuevo entorno virtual. Si utilizas un sistema operativo tipo UNIX, puedes usar:

~ " python3 -m venv env && source env/bin/activate              

Ahora solo tienes que crear un nuevo archivo de Python que contenga nuestra lógica y abrirlo en el IDE que prefieras. Si quieres usar el terminal, solo tienes que pegar el siguiente comando:

~ » touch scraper.py && code .                                         

2. Instalación de dependencias

Usaremos pip para instalar los paquetes que necesitamos para este proyecto. Por ahora, hemos decidido que vamos a utilizar Requests, pero no es suficiente para un rastreador web. Un rastreador web también implica el manejo de los datos. Esto significa que necesitamos analizar el HTML recopilado de los servidores. Por suerte, la biblioteca de Python ofrece una gran variedad de paquetes. Sin embargo, para este proyecto utilizaremos BeautifulSoup. Para instalar los paquetes, simplemente pega el siguiente comando:

~ » python3 -m pip install requests bs4                                    

3. Redactar la lógica

Dividiremos nuestro código en dos secciones: una para la extracción de datos y otra para la manipulación de datos. La primera parte se lleva a cabo con el paquete Requests, mientras que la segunda se realiza con BeautifulSoup. Sin más preámbulos, pasemos a la programación, empezando por la parte de la extracción:

import requests

def scrape(url = None):
   # Si no hay ninguna URL, no es necesario utilizar los clientes HTTP de Python
   # Mostraremos un mensaje y detendremos la ejecución
   if url == None:
       print('[!] ¡Por favor, añade un destino!')
       return

   response = requests.get(url)
   return response

En esta sección, definimos una función con un único parámetro: la URL de destino. Si no se proporciona la URL, mostraremos un mensaje y detendremos la ejecución. En caso contrario, utilizaremos el método `get` de `Request` para devolver la respuesta. Ahora bien, sabemos que los clientes HTTP de Python admiten más métodos, así que añadamos un parámetro condicional:

import requests

def scrape(method = 'get', url = None, data = None):
   # Si no hay ninguna URL, no es necesario utilizar clientes HTTP de Python
   # Mostraremos un mensaje y detendremos la ejecución
   if url == None:
       print('[!] ¡Por favor, añade un destino!')
       return

   if method.lower() == 'get':
       response = requests.get(url)

   elif method.lower() == 'post':
       if data == None:
           print('[!] ¡Añade una carga útil a tu solicitud POST!')
           return
       response = requests.post( url, data )
      
   return response

Como puedes ver, hemos añadido un par de parámetros más a nuestra función. El parámetro `method` especifica qué método se debe utilizar para nuestra solicitud. El parámetro `data` representa la carga útil que enviamos con la solicitud POST. Por defecto, el método es GET, por lo que el parámetro `method` no es obligatorio. 

Reto: Añade más métodos a esta función y amplía las capacidades de nuestro rastreador. No solo es divertido, sino que también es una buena forma de aprender. Además, podrás personalizar el código para añadirlo a tu portfolio.

Hasta ahora hemos hablado de la extracción de datos. Analicemos el código HTML y hagamos algo con él:

from bs4 import BeautifulSoup

def extract_elements(data = None, el = None):
   if data == None:
       print('[!] ¡Por favor, añade algunos datos!')
       return

   if el == None:
       print('[!] ¡Especifique qué elementos desea seleccionar!')
       return

   soup = BeautifulSoup(data.text, 'html.parser')
   elements = soup.find_all(el)

   return elements

Pero un rastreador web debería ser capaz de extraer datos más específicos. Por ejemplo, debería poder localizar y devolver elementos basándose en su selector CSS. Así que vamos a añadir la lógica que se encarga de esta parte:

from bs4 import BeautifulSoup

def extract_elements(data = None, el = None, attr = None, attr_value = None):
   if data == None:
       print('[!] Please add some data!')
       return

   if el == None:
       print('[!] Please specify which elements you are targeting!')
       return

   soup = BeautifulSoup(data.text, 'html.parser')
   elements = soup.find_all(el, { attr : attr_value })

   return elements

BeautifulSoup nos permite extraer datos específicos en función de sus atributos. Por eso, aquí hemos añadido dos nuevos parámetros que nos ayudarán a localizar y extraer elementos en función de sus atributos.

Ya tenemos todo lo necesario. Solo nos queda combinar las dos secciones y ya tendremos nuestro rastreador web. Una vez que hayas montado el código, solo tienes que:

  • Crea una nueva variable que contenga los datos extraídos con Requests 
  • Imprimir los elementos devueltos por BeautifulSoup

Aquí tienes las dos partes que faltaban en tu código:

data = scrape('GET', 'https://webscrapingapi.com')
print( extract_elements(data, 'ul') )

Seguro que ya te has dado cuenta de para qué sirve cada cosa y que, a estas alturas, no hace falta ninguna traducción. Al igual que con nuestro scraper, te reto a que experimentes con la función `extract_elements` y consigas que haga algo más que simplemente devolver elementos. 

Conclusión

Cuando se aprende un nuevo concepto de programación, creo que lo mejor es probarlo con las diferentes tecnologías disponibles. Sin embargo, a la hora de crear realmente la infraestructura para un proyecto más amplio, es mejor conocer los puntos fuertes y débiles de cada tecnología antes de elegir una. 

Espero que este artículo te haya servido de ayuda y que ahora tengas una idea clara de cómo funcionan los clientes HTTP de Python. También te animo a que experimentes un poco, ya que estoy seguro de que encontrarás el paquete que más te conviene.

Acerca del autor
Mihnea-Octavian Manolache, desarrollador full stack en WebScrapingAPI
Mihnea-Octavian ManolacheDesarrollador Full Stack

Mihnea-Octavian Manolache es ingeniero Full Stack y DevOps en WebScrapingAPI, donde se encarga de desarrollar funciones para los productos y de mantener la infraestructura que garantiza el buen funcionamiento de la plataforma.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.