Guía para usar cURL con Python

En este artículo, aprenderá cómo Python y cURL pueden usarse juntos para automatizar peticiones GET, POST y PUT, y para descargar archivos y páginas web.
14 min read
Guide to using cURL with Python

cURL es una herramienta versátil de línea de comandos, es de fuente abierta y sirve para transferir datos a través de una red. Viene con una gran variedad de parámetros para el manejo de casi cualquier solicitud. Además, cURL es expansible y tiene una interfaz básicamente en todos los lenguajes de programación modernos.

El uso de cURL con un lenguaje de programación ofrece muchas ventajas. Por ejemplo, se puede automatizar la realización de peticiones para casos de uso de depuración o raspado web.

En este artículo, aprenderá cómo Python y cURL pueden usarse juntos para automatizar peticiones GET, POST y PUT, y para descargar archivos y páginas web.

¿Qué es cURL?  

cURL es un proyecto de software, pero su nombre también se utiliza en dos productos: una biblioteca conocida como libcurl y una herramienta de línea de comandos conocida como curl (que utiliza libcurl). Cuando se hace referencia a curl en este artículo, se trata de la herramienta de línea de comandos.

curl se considera versátil; sin embargo, su tarea principal es simple: transferir datos a través de varios protocolos de red. Dada la complejidad actual de la web, curl viene con una enorme lista de opciones para manejar las peticiones más complejas.

curl se lanzó por primera vez en 1996 como HttpGet y más tarde se llamó urlget antes de convertirse en curl. Su primer caso de uso fue la obtención de tipos de cambio de divisas para su uso en un canal IRC. Hoy en día, curl permite transferir datos a través de diversos métodos, como FTP(S), HTTP(S) (POST, GET, PUT), IMAP, POP3, MQTT y SMB. Además, curl puede gestionar cookies y certificados SSL.

Cuando curl establece una conexión a través de HTTPS, obtiene el certificado del servidor remoto y lo comprueba con su almacén de certificados CA para asegurarse de que el servidor remoto es el que dice ser. Por ejemplo, la siguiente petición envía una petición HTTPS al sitio web de Bright Data y establece una cookie conocida como greeting con el valor hello:

curl --cookie "greeting=hello" https://www.brightdata.com

¿Por qué usar curl con Python?

Aunque curl es una herramienta versátil, hay una razón principal por la que querría usarla con Python: Python puede automatizar sus peticiones. A continuación, se presentan tres casos de uso donde esta es una combinación valiosa:  

Raspado de datos

El raspado de datos es la práctica de recopilar (a menudo grandes) cantidades de datos de una o más páginas web. Para raspar datos con Python, la gente a menudo confía en la biblioteca de peticiones. Para el raspado recursivo, se puede utilizar wget. Sin embargo, para casos de uso de raspado web avanzado con llamadas HTTP(S) complejas, curl con Python es ideal.

Los datos de una página web se pueden recopilar con un solo comando curl que genera y procesa una solicitud HTTP(S), sin embargo, no puede hacerse de manera recursiva. Al integrar curl en código Python, se puede simular una ruta de navegación en una página web manipulando elementos, como los parámetros de la petición, las cookies y los agentes de usuario.

La navegación ni siquiera tiene que ser fija. Al hacerla dependiente del contenido raspado, cada nueva petición puede ser totalmente dinámica.

Por ejemplo, al raspar la sección de comentarios de un sitio web popular de noticias, cuando sólo se desea raspar la página de perfil del autor, si el comentario contiene palabras clave insultantes, se puede crear un enunciado condicional que dependa de los comentarios raspados y aplicar fácilmente este filtro dinámico.

Además, muchos sitios web disponen de mecanismos de seguridad que dificultan el raspado de muchas páginas: como la protección contra la denegación de servicio distribuida (DDoS) o un aviso reCAPTCHA. Aplicando ciertas reglas y pausas entre peticiones, se puede simular un comportamiento humano más difícil de detectar.

Pruebas y depuración

Usar curl en su propio sitio web parece una tontería, pero es útil en un contexto de pruebas y depuración. Probar o depurar una o varias características de una aplicación suele ser una tarea engorrosa. Es necesario probarla de forma recurrente y con una gran variedad de configuraciones o parámetros. Aunque hay muchas herramientas de prueba disponibles, Python y curl facilitan la configuración de algunas pruebas rápidas.

Por ejemplo, podría tomar años probar manualmente todas las variaciones si se está lanzando un nuevo flujo de pago para un servicio en línea (complejo) que utiliza cookies, se basa en el referente, tiene pequeñas diferencias por navegador (es decir, agente de usuario), y empaqueta todos los pasos del flujo de pago en el cuerpo de una solicitud POST. En Python, se puede crear un diccionario que contenga todo el conjunto de parámetros y enviar una solicitud utilizando curl para cada combinación posible.

Automatización del flujo de trabajo

Además de pruebas, depuración y raspado web, los casos de uso de curl incluye automatización de flujos de trabajo. Por ejemplo, muchos procesos de integración de datos comienzan con un volcado recurrente de una exportación de datos, como un archivo CSV o Apache Parquet. El copiado de volcados de datos puede automatizarse por completo usando una aplicación Python que busque nuevos archivos en un servidor (S)FTP.

O considere la posibilidad de configurar mailhooks. Imagine cuántas tareas diarias podrían automatizarse si una aplicación pudiera sondear los mensajes de correo electrónico que contienen una consulta. Se puede lograr que las aplicaciones Python se activen cuando un buzón reciba un correo electrónico específico mediante el sondeo de nuevos mensajes a través del protocolo POP3 o IMAP.

Cómo usar cURL con Python

Hay varias formas de hacer peticiones usando curl en Python. Este artículo cubre dos opciones. La primera es simular peticiones curl en la línea de comandos a través de los paquetes os y subprocess de Python. Este enfoque directo envía comandos programáticamente a la interfaz de línea de comandos de su sistema operativo.

La segunda opción es utilizar el paquete PycURL. Si le interesa conocer otras formas de raspar sitios web con Python (sin usar curl), puede consultar la guía de Bright Data Raspado de datos con Python.

Requisitos previos

Antes de comenzar este tutorial, asegúrese de haber descargado e instalado curl. Si usa Windows, asegúrese de añadir curl a su variable de entorno PATH para que simplemente pueda ejecutar el comando curl.

Para hacer una interfaz Python con su sistema operativo, puede usar varios paquetes. Sin embargo, los dos más populares son os y subprocess. Para instalar ambos, ejecute el siguiente comando pip:

pip install os subprocess

Hacer una petición usando curl y os

El paquete os es un paquete extremadamente simple. Ejecutar una petición curl sin procesar la respuesta solo requiere dos líneas de código. Solo es necesario pasar la cookie descrita en el ejemplo anterior, y la salida se escribe en el archivo output.txt:

import os
os.system('curl -o output.txt --cookie "greeting=hello" -k https://curl.se')

Si desea procesar la respuesta en Python en lugar de escribirla en un archivo, debe utilizar el paquete subprocess que se aborda en la siguiente sección.

El siguiente código ejecutará el mismo enunciado, pero en lugar de escribir la respuesta en un archivo, mostrará stdout y stderr como una tupla. Esta salida puede procesarse con otros paquetes de Python, como Beautiful Soup:

import shlex
import subprocess
shell_cmd = shlex.split('curl --cookie "greeting=hello" -k https://curl.se')
process = subprocess.Popen(shell_cmd,
                    stdout = subprocess.PIPE,
                    stderr = subprocess.PIPE,
                    text = True,
                    shell = True
                    )
std_out, std_err = process.communicate()
std_out.strip(), std_err

Usando PycURL

En lugar de interactuar con la terminal en Python, se puede usar el paquete PycURL. Si es usuario de Linux, tiene suerte ya que puede instalar PycURL usando pip:

pip install pycurl
pip install certifi

También se debería instalar certifi para interactuar sobre el protocolo HTTPS. Si encuentra dificultades, siga estas instrucciones de Stack Overflow.

Aunque PycURL también se puede instalar en Windows, es una tarea muy frustrante. Si se intenta instalar a través de pip, devolverá el siguiente error:

Please specify --curl-dir=/path/to/built/libcurl

Por eso es necesario instalarlo desde el código fuente, lo cual “no es para los débiles de corazón debido a la multitud de posibles dependencias y a que cada una de estas dependencias tiene su propia estructura de directorios, estilo de configuración, parámetros y peculiaridades“. Por esta razón, se recomienda utilizar el paquete requests para las peticiones básicas de red si está trabajando en una máquina Windows.

Cómo hacer peticiones con PycURL

El resto de este artículo explica cómo crear varios tipos de peticiones utilizando el paquete PycURL.

Haciendo una petición GET con PycURL

La petición más sencilla que se puede hacer con PycURL es una petición GET. Es básicamente una plantilla para todas las demás plantillas en esta sección.

En el código a continuación, se puede identificar cinco pasos:

  1. Se importan todos los paquetes necesarios.
  2. Se crean dos objetos: el buffer en el que la petición curl almacenará su respuesta y el objeto curl, que se utiliza para hacer la petición.
  3. Se especifican las opciones de la petición: la URL, el destino y la validación SSL.
  4. La ejecución de la petición.
  5. La salida de la petición.
# Preparation
import pycurl
import certifi
from io import BytesIO

# Set buffer and Curl object.
buffer = BytesIO()
c = pycurl.Curl()

# Set request options.
## Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/')

## Set the buffer as the destination of the request's response.
c.setopt(c.WRITEDATA, buffer)

## Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
c.perform()
c.close()

# Print the buffer's content with a Latin1 (iso-8859-1) encoding.
body = buffer.getvalue()
data = body.decode('iso-8859-1')
print(data)

Realizar una petición POST con PycURL

Realizar una petición POST con PycURL es muy similar a realizar una petición GET. Sin embargo, se añade una opción extra a la petición: el cuerpo POST. En el siguiente fragmento de código, se establece un valor-clave y se codifica la URL para asegurar que se procesa adecuadamente:

# Preparation
import pycurl
import certifi
from io import BytesIO

# Set buffer and Curl object.
buffer = BytesIO()
c = pycurl.Curl()

# Set request options.
## Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/')

## Set the request's body.
post_body = {'greeting': 'hello'}
postfields = urlencode(post_body)
c.setopt(c.POSTFIELDS, postfields)

## Set the buffer as the destination of the request's response.
c.setopt(c.WRITEDATA, buffer)

## Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
c.perform()
c.close()

# Print the buffer's content with a Latin1 (iso-8859-1) encoding.
body = buffer.getvalue()
print(body.decode('iso-8859-1'))

Realizar una petición PUT con PycURL

La petición POST creada en la sección anterior también puede enviarse como una petición PUT. En lugar de enviar el key-value en el cuerpo de la petición, se enviará como una representación de fichero codificada en UTF-8. Este método también se puede utilizar para subir archivos:

import pycurl
import certifi
from io import BytesIO

c = pycurl.Curl()

# Set request options.
## Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/')

## Set data for the PUT request.
c.setopt(c.UPLOAD, 1)
data = '{"greeting": "hello"}'
buffer = BytesIO(data.encode('utf-8'))
c.setopt(c.READDATA, buffer)

## Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
c.perform()
c.close()

Descarga de un archivo con PycURL

El siguiente fragmento demuestra cómo se puede descargar un fichero utilizando PycURL. Se solicita una imagen JPEG aleatoria, y se abre un flujo de escritura a alguna_imagen.jpg y se pasa a PycURL como destino del archivo:

import pycurl
import certifi

c = pycurl.Curl()

# Set the request destination.
c.setopt(c.URL, 'http://pycurl.io/some_image.jpg')

# Refer to the installed certificate authority bundle for validating the SSL certificate.
c.setopt(c.CAINFO, certifi.where())

# Execute and close the request.
with open('some_image.jpg', 'w') as f:
    c.setopt(c.WRITEFUNCTION, f.write)
    c.perform()

c.close()

Descarga y procesamiento de una página web con PycURL

Dado que muchos de los casos de uso de PycURL implican raspado web, el siguiente fragmento describe cómo se puede procesar la respuesta de una petición con Beautiful Soup, un popular paquete para analizar archivos HTML.

Primero, se instala Beautiful Soup 4 usando pip:

pip install beautifulsoup4

Segundo, se pone el siguiente fragmento detrás del primer fragmento PycURL que hizo una petición GET. Esto hará que Beautiful Soup procese los datos de respuesta.

Para la demostración, se utiliza el método find_all para encontrar todos los elementos del párrafo, y se imprime el contenido de los párrafos individuales:

from bs4 import BeautifulSoup

# Parsing data using BeautifulSoup
soup = BeautifulSoup(data, 'html.parser')

# Find all paragraphs
paragraphs = soup.find_all('p')
for p in paragraphs:
   print(p.text)

Uso de un Proxy con PycURL

El raspado web a escala funciona mejor cuando se trabaja con proxies. La ventaja es que se puede emular el comportamiento de navegación en paralelo sin que su raspador sea señalado como bot o como que realiza un comportamiento anómalo.

En esta sección final, aprenderá cómo puede crear una petición con PycURL a través de un proxy. Esto se consigue ajustando las opciones de petición, como hizo anteriormente. A continuación, se describen cuatro ajustes, pero se pueden ajustar a cada situación:

  1. Para facilitar las cosas, se habilitan los proxies no seguros.
  2. Se configura el servidor proxy.
  3. El script se autentica con el servidor.
  4. El proxy se configura como HTTPS.
# Enable insecure proxies
c.setopt(c.PROXY_SSL_VERIFYHOST, 0)
c.setopt(c.PROXY_SSL_VERIFYPEER, 0)

# Set proxy server
c.setopt(pycurl.PROXY, <YOUR_HTTPS_PROXY_SERVER>)

# Authenticate with the proxy server
c.setopt(pycurl.PROXYUSERPWD, f"{<YOUR_USERNAME>}:{<YOUR_PASSWORD>}")

# Set proxy type to https
c.setopt(pycurl.PROXYTYPE, 2)

Estas opciones se pueden insertar en cualquier parte del fragmento de código descrito anteriormente para hacer que la solicitud se redirija a través del servidor proxy.

Conclusión

En este artículo, se explicó a detalle la combinación de curl y Python, con énfasis en la razón por la qué se querrían usar juntos para generar peticiones complejas para casos de uso de raspado web y pruebas de aplicaciones. Se proporcionaron múltiples ejemplos para demostrar la versatilidad de PycURL para generar una multitud de peticiones de red.

Alternativamente, se puede hacer uso de Bright Data Proxy Network y su IDE Web Scraper que fue específicamente diseñado para manejar todo el trabajo pesado para los desarrolladores. De este modo, es posible centrarse en trabajar con los datos raspados en lugar de preocuparse por sortear los mecanismos antiraspado.