Introducción
La biblioteca Python requests es una herramienta poderosa para interactuar con servicios web y APIs. En este tutorial, aprenderás a enviar peticiones HTTP y a analizar los datos de respuesta utilizando Python. Al final de este laboratorio, serás capaz de extraer información valiosa de diferentes tipos de respuestas de API, lo que te permitirá construir aplicaciones basadas en datos y automatizar interacciones web.
Instalación de la Biblioteca Requests y Realización de una Petición Básica
En este primer paso, instalaremos la biblioteca Python requests y realizaremos nuestra primera petición HTTP para recuperar datos de una API pública.
Instalación de Requests
La biblioteca requests es un paquete de terceros que necesita ser instalado usando pip, el instalador de paquetes de Python. Comencemos instalándola:
pip install requests
Deberías ver una salida que confirme que requests se instaló correctamente.
Realización de tu Primera Petición HTTP
Ahora, creemos un archivo Python para realizar una petición HTTP simple. En el WebIDE, crea un nuevo archivo llamado basic_request.py en el directorio /home/labex/project.
Añade el siguiente código al archivo:
import requests
## Realiza una petición GET a una API pública
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
## Imprime el código de estado
print(f"Código de estado: {response.status_code}")
## Imprime el contenido de la respuesta sin procesar
print("\nContenido de la respuesta sin procesar:")
print(response.text)
## Imprime las cabeceras de la respuesta
print("\nCabeceras de la respuesta:")
for header, value in response.headers.items():
print(f"{header}: {value}")
Este código realiza una petición GET a un endpoint (punto final) de API de ejemplo e imprime información sobre la respuesta.
Comprensión del Objeto Response
Ejecutemos el código para ver qué información obtenemos. En la terminal, ejecuta:
python basic_request.py
Deberías ver una salida similar a esta:
Código de estado: 200
Contenido de la respuesta sin procesar:
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
Cabeceras de la respuesta:
Date: Mon, 01 Jan 2023 12:00:00 GMT
Content-Type: application/json; charset=utf-8
...
El objeto response contiene varios atributos importantes:
status_code: Código de estado HTTP (200 significa éxito)text: El contenido de la respuesta como una cadena de texto (string)headers: Un diccionario de cabeceras de respuesta
Al trabajar con peticiones web, estos atributos te ayudan a comprender la respuesta del servidor y a manejarla apropiadamente.
Códigos de Estado HTTP
Los códigos de estado HTTP indican si una petición tuvo éxito o falló:
- 2xx (como 200): Éxito
- 3xx (como 301): Redirección
- 4xx (como 404): Errores del cliente
- 5xx (como 500): Errores del servidor
Modifiquemos nuestro código para verificar una respuesta exitosa. Crea un nuevo archivo llamado check_status.py con este contenido:
import requests
try:
## Realiza una petición GET a una URL válida
response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
## Verifica si la petición fue exitosa
if response.status_code == 200:
print("¡Petición exitosa!")
else:
print(f"La petición falló con el código de estado: {response.status_code}")
## Prueba una URL inválida
invalid_response = requests.get("https://jsonplaceholder.typicode.com/invalid")
print(f"Código de estado de URL inválida: {invalid_response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Ocurrió un error: {e}")
Ejecuta este código para ver cómo diferentes URLs devuelven diferentes códigos de estado:
python check_status.py
Deberías ver que la URL válida devuelve el código de estado 200, mientras que la URL inválida devuelve el código de estado 404.
Análisis de Datos de Respuesta JSON
Muchas APIs modernas devuelven datos en formato JSON (JavaScript Object Notation). En este paso, aprenderás a analizar respuestas JSON y a trabajar con los datos en Python.
Comprensión de JSON
JSON es un formato ligero de intercambio de datos que es fácil de leer y escribir para los humanos, y fácil de analizar y generar para las máquinas. Se basa en pares clave-valor, similar a los diccionarios de Python.
Aquí hay un ejemplo de un objeto JSON:
{
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"is_active": true,
"hobbies": ["reading", "swimming", "cycling"]
}
Análisis de Respuestas JSON
La biblioteca requests facilita el análisis de respuestas JSON utilizando el método .json(). Creemos un nuevo archivo llamado parse_json.py y agreguemos el siguiente código:
import requests
## Realiza una petición a un endpoint de la API de GitHub que devuelve datos JSON
response = requests.get("https://api.github.com/users/python")
## Verifica si la petición fue exitosa
if response.status_code == 200:
## Analiza la respuesta JSON
data = response.json()
## Imprime los datos analizados
print("Datos JSON analizados:")
print(f"Nombre de usuario: {data['login']}")
print(f"Nombre: {data.get('name', 'No proporcionado')}")
print(f"Seguidores: {data['followers']}")
print(f"Repositorios públicos: {data['public_repos']}")
## Imprime el tipo para verificar que es un diccionario de Python
print(f"\nTipo de datos analizados: {type(data)}")
## Accede a datos anidados
print("\nAccediendo a elementos específicos:")
print(f"URL del avatar: {data['avatar_url']}")
else:
print(f"La petición falló con el código de estado: {response.status_code}")
Ejecuta este script para ver cómo los datos JSON se analizan en un diccionario de Python:
python parse_json.py
Deberías ver una salida que muestra información sobre el usuario de GitHub, incluyendo su nombre de usuario, el conteo de seguidores y el conteo de repositorios.
Trabajando con Listas de Datos
Muchas APIs devuelven listas de objetos. Veamos cómo manejar este tipo de respuesta. Crea un archivo llamado json_list.py con este contenido:
import requests
## Realiza una petición a una API que devuelve una lista de posts
response = requests.get("https://jsonplaceholder.typicode.com/posts")
## Verifica si la petición fue exitosa
if response.status_code == 200:
## Analiza la respuesta JSON (esto será una lista de posts)
posts = response.json()
## Imprime el número total de posts
print(f"Total de posts: {len(posts)}")
## Imprime detalles de los primeros 3 posts
print("\nPrimeros 3 posts:")
for i, post in enumerate(posts[:3], 1):
print(f"\nPost #{i}")
print(f"User ID: {post['userId']}")
print(f"Post ID: {post['id']}")
print(f"Título: {post['title']}")
print(f"Cuerpo: {post['body'][:50]}...") ## Imprime solo el principio del cuerpo
else:
print(f"La petición falló con el código de estado: {response.status_code}")
Ejecuta este script para ver cómo procesar una lista de objetos JSON:
python json_list.py
Deberías ver información sobre los primeros tres posts, incluyendo sus títulos y el principio de su contenido.
Manejo de Errores con el Análisis JSON
A veces, una respuesta podría no contener datos JSON válidos. Veamos cómo manejar esto de manera adecuada. Crea un archivo llamado json_error.py con este código:
import requests
import json
def get_and_parse_json(url):
try:
## Realiza la petición
response = requests.get(url)
## Verifica si la petición fue exitosa
response.raise_for_status()
## Intenta analizar el JSON
try:
data = response.json()
return data
except json.JSONDecodeError:
print(f"La respuesta de {url} no es JSON válido")
print(f"Respuesta sin procesar: {response.text[:100]}...") ## Imprime parte de la respuesta sin procesar
return None
except requests.exceptions.HTTPError as e:
print(f"Error HTTP: {e}")
except requests.exceptions.RequestException as e:
print(f"Error de petición: {e}")
return None
## Prueba con un endpoint JSON válido
json_data = get_and_parse_json("https://jsonplaceholder.typicode.com/posts/1")
if json_data:
print("\nRespuesta JSON válida:")
print(f"Título: {json_data['title']}")
## Prueba con un endpoint que no es JSON
html_data = get_and_parse_json("https://www.example.com")
if html_data:
print("\nEsto no debería imprimirse ya que example.com devuelve HTML, no JSON")
else:
print("\nComo se esperaba, no se pudo analizar HTML como JSON")
Ejecuta este script para ver cómo manejar diferentes tipos de respuestas:
python json_error.py
Deberías ver que el código maneja con éxito tanto las respuestas JSON válidas como las respuestas que no son JSON.
Análisis de Contenido HTML con BeautifulSoup
Al trabajar con datos web, a menudo te encontrarás con respuestas HTML. Para analizar HTML, la biblioteca BeautifulSoup de Python es una herramienta excelente. En este paso, aprenderemos a extraer información de las respuestas HTML.
Instalación de BeautifulSoup
Primero, instalemos BeautifulSoup y su analizador HTML:
pip install beautifulsoup4
Análisis HTML Básico
Creemos un archivo llamado parse_html.py para obtener y analizar una página web:
import requests
from bs4 import BeautifulSoup
## Realiza una petición a una página web
url = "https://www.example.com"
response = requests.get(url)
## Verifica si la petición fue exitosa
if response.status_code == 200:
## Analiza el contenido HTML
soup = BeautifulSoup(response.text, 'html.parser')
## Extrae el título de la página
title = soup.title.text
print(f"Título de la página: {title}")
## Extrae todos los párrafos
paragraphs = soup.find_all('p')
print(f"\nNúmero de párrafos: {len(paragraphs)}")
## Imprime el texto del primer párrafo
if paragraphs:
print(f"\nTexto del primer párrafo: {paragraphs[0].text.strip()}")
## Extrae todos los enlaces
links = soup.find_all('a')
print(f"\nNúmero de enlaces: {len(links)}")
## Imprime el atributo href del primer enlace
if links:
print(f"Href del primer enlace: {links[0].get('href')}")
else:
print(f"La petición falló con el código de estado: {response.status_code}")
Ejecuta este script para ver cómo extraer información básica de una página HTML:
python parse_html.py
Deberías ver una salida que muestra el título de la página, el número de párrafos, el texto del primer párrafo, el número de enlaces y la URL del primer enlace.
Encontrar Elementos Específicos
Ahora veamos cómo encontrar elementos específicos usando selectores CSS. Crea un archivo llamado html_selectors.py:
import requests
from bs4 import BeautifulSoup
## Realiza una petición a una página web con una estructura más compleja
url = "https://quotes.toscrape.com/"
response = requests.get(url)
## Verifica si la petición fue exitosa
if response.status_code == 200:
## Analiza el contenido HTML
soup = BeautifulSoup(response.text, 'html.parser')
## Encuentra todos los elementos de cita
quote_elements = soup.select('.quote')
print(f"Número de citas encontradas: {len(quote_elements)}")
## Procesa las primeras 3 citas
print("\nPrimeras 3 citas:")
for i, quote_element in enumerate(quote_elements[:3], 1):
## Extrae el texto de la cita
text = quote_element.select_one('.text').text
## Extrae el autor
author = quote_element.select_one('.author').text
## Extrae las etiquetas
tags = [tag.text for tag in quote_element.select('.tag')]
## Imprime la información
print(f"\nCita #{i}")
print(f"Texto: {text}")
print(f"Autor: {author}")
print(f"Etiquetas: {', '.join(tags)}")
else:
print(f"La petición falló con el código de estado: {response.status_code}")
Ejecuta este script para ver cómo usar selectores CSS para extraer elementos específicos:
python html_selectors.py
Deberías ver una salida que muestra información sobre las primeras tres citas, incluyendo el texto de la cita, el autor y las etiquetas.
Construyendo un Rascador Web Simple
Juntemos todo para construir un rascador web simple que extrae datos estructurados de una página web. Crea un archivo llamado quotes_scraper.py:
import requests
from bs4 import BeautifulSoup
import json
import os
def scrape_quotes_page(url):
## Realiza una petición a la página web
response = requests.get(url)
## Verifica si la petición fue exitosa
if response.status_code != 200:
print(f"La petición falló con el código de estado: {response.status_code}")
return None
## Analiza el contenido HTML
soup = BeautifulSoup(response.text, 'html.parser')
## Extrae todas las citas
quotes = []
for quote_element in soup.select('.quote'):
## Extrae el texto de la cita
text = quote_element.select_one('.text').text.strip('"')
## Extrae el autor
author = quote_element.select_one('.author').text
## Extrae las etiquetas
tags = [tag.text for tag in quote_element.select('.tag')]
## Añade la cita a nuestra lista
quotes.append({
'text': text,
'author': author,
'tags': tags
})
## Verifica si hay una página siguiente
next_page = soup.select_one('.next a')
next_page_url = None
if next_page:
next_page_url = 'https://quotes.toscrape.com' + next_page['href']
return {
'quotes': quotes,
'next_page': next_page_url
}
## Raspa la primera página
result = scrape_quotes_page('https://quotes.toscrape.com/')
if result:
## Imprime información sobre las citas encontradas
quotes = result['quotes']
print(f"Encontradas {len(quotes)} citas en la primera página")
## Imprime las primeras 2 citas
print("\nPrimeras 2 citas:")
for i, quote in enumerate(quotes[:2], 1):
print(f"\nCita #{i}")
print(f"Texto: {quote['text']}")
print(f"Autor: {quote['author']}")
print(f"Etiquetas: {', '.join(quote['tags'])}")
## Guarda las citas en un archivo JSON
output_dir = '/home/labex/project'
with open(os.path.join(output_dir, 'quotes.json'), 'w') as f:
json.dump(quotes, f, indent=2)
print(f"\nGuardadas {len(quotes)} citas en {output_dir}/quotes.json")
## Imprime información sobre la página siguiente
if result['next_page']:
print(f"\nURL de la página siguiente: {result['next_page']}")
else:
print("\nNo hay página siguiente disponible")
Ejecuta este script para raspar citas de un sitio web:
python quotes_scraper.py
Deberías ver una salida que muestra información sobre las citas encontradas en la primera página, y las citas se guardarán en un archivo JSON llamado quotes.json.
Revisa el archivo JSON para ver los datos estructurados:
cat quotes.json
El archivo debería contener un array JSON de objetos de cita, cada uno con propiedades de texto, autor y etiquetas.
Trabajando con Contenido de Respuesta Binario
Hasta ahora, nos hemos centrado en respuestas basadas en texto como JSON y HTML. Sin embargo, la biblioteca requests también puede manejar contenido binario como imágenes, PDFs y otros archivos. En este paso, aprenderemos a descargar y procesar contenido binario.
Descargando una Imagen
Comencemos descargando una imagen. Crea un archivo llamado download_image.py:
import requests
import os
## URL de una imagen para descargar
image_url = "https://httpbin.org/image/jpeg"
## Realiza una petición para obtener la imagen
response = requests.get(image_url)
## Verifica si la petición fue exitosa
if response.status_code == 200:
## Obtiene el tipo de contenido
content_type = response.headers.get('Content-Type', '')
print(f"Tipo de contenido: {content_type}")
## Verifica si el contenido es una imagen
if 'image' in content_type:
## Crea un directorio para guardar la imagen si no existe
output_dir = '/home/labex/project/downloads'
os.makedirs(output_dir, exist_ok=True)
## Guarda la imagen en un archivo
image_path = os.path.join(output_dir, 'sample_image.jpg')
with open(image_path, 'wb') as f:
f.write(response.content)
## Imprime información sobre la imagen guardada
print(f"Imagen guardada en: {image_path}")
print(f"Tamaño de la imagen: {len(response.content)} bytes")
else:
print("La respuesta no contiene una imagen")
else:
print(f"La petición falló con el código de estado: {response.status_code}")
Ejecuta este script para descargar una imagen:
python download_image.py
Deberías ver una salida que confirma que la imagen fue descargada y guardada en /home/labex/project/downloads/sample_image.jpg.
Descargando un Archivo con Progreso
Al descargar archivos grandes, puede ser útil mostrar un indicador de progreso. Creemos un script que muestre el progreso de la descarga. Crea un archivo llamado download_with_progress.py:
import requests
import os
import sys
def download_file(url, filename):
## Realiza una petición para obtener el archivo
## Transmite la respuesta para manejar archivos grandes de manera eficiente
response = requests.get(url, stream=True)
## Verifica si la petición fue exitosa
if response.status_code != 200:
print(f"La petición falló con el código de estado: {response.status_code}")
return False
## Obtiene el tamaño total del archivo si está disponible
total_size = int(response.headers.get('Content-Length', 0))
if total_size:
print(f"Tamaño total del archivo: {total_size/1024:.2f} KB")
else:
print("Encabezado Content-Length no encontrado. No se puede determinar el tamaño del archivo.")
## Crea un directorio para guardar el archivo si no existe
os.makedirs(os.path.dirname(filename), exist_ok=True)
## Descarga el archivo en fragmentos y muestra el progreso
print(f"Descargando {url} a {filename}...")
## Inicializa variables para el seguimiento del progreso
downloaded = 0
chunk_size = 8192 ## Fragmentos de 8 KB
## Abre el archivo para escribir
with open(filename, 'wb') as f:
## Itera a través de los fragmentos de la respuesta
for chunk in response.iter_content(chunk_size=chunk_size):
if chunk: ## Filtra los fragmentos keep-alive
f.write(chunk)
downloaded += len(chunk)
## Calcula y muestra el progreso
if total_size:
percent = downloaded * 100 / total_size
sys.stdout.write(f"\rProgreso: {percent:.1f}% ({downloaded/1024:.1f} KB)")
sys.stdout.flush()
else:
sys.stdout.write(f"\rDescargado: {downloaded/1024:.1f} KB")
sys.stdout.flush()
## Imprime una nueva línea para asegurar que la siguiente salida comience en una nueva línea
print()
return True
## URL de un archivo para descargar
file_url = "https://speed.hetzner.de/100MB.bin"
## Ruta donde se guardará el archivo
output_path = '/home/labex/project/downloads/test_file.bin'
## Descarga el archivo
success = download_file(file_url, output_path)
if success:
## Obtiene las estadísticas del archivo
file_size = os.path.getsize(output_path)
print(f"\n¡Descarga completa!")
print(f"Archivo guardado en: {output_path}")
print(f"Tamaño del archivo: {file_size/1024/1024:.2f} MB")
else:
print("\nDescarga fallida.")
Ejecuta este script para descargar un archivo con seguimiento del progreso:
python download_with_progress.py
Verás una barra de progreso actualizándose a medida que se descarga el archivo. Ten en cuenta que esto descarga un archivo de 100MB, lo que podría llevar algún tiempo dependiendo de la velocidad de tu conexión.
Para cancelar la descarga, puedes presionar Ctrl+C.
Trabajando con Encabezados de Respuesta y Metadatos
Al descargar archivos, los encabezados de respuesta a menudo contienen metadatos útiles. Creemos un script que examine los encabezados de respuesta en detalle. Crea un archivo llamado response_headers.py:
import requests
def check_url(url):
print(f"\nComprobando URL: {url}")
try:
## Realiza una petición HEAD primero para obtener los encabezados sin descargar el contenido completo
head_response = requests.head(url)
print(f"Código de estado de la petición HEAD: {head_response.status_code}")
if head_response.status_code == 200:
## Imprime todos los encabezados
print("\nEncabezados de respuesta:")
for header, value in head_response.headers.items():
print(f" {header}: {value}")
## Extrae el tipo de contenido y el tamaño
content_type = head_response.headers.get('Content-Type', 'Desconocido')
content_length = head_response.headers.get('Content-Length', 'Desconocido')
print(f"\nTipo de contenido: {content_type}")
if content_length != 'Desconocido':
size_kb = int(content_length) / 1024
size_mb = size_kb / 1024
if size_mb >= 1:
print(f"Tamaño del contenido: {size_mb:.2f} MB")
else:
print(f"Tamaño del contenido: {size_kb:.2f} KB")
else:
print("Tamaño del contenido: Desconocido")
## Verifica si el servidor soporta peticiones de rango
accept_ranges = head_response.headers.get('Accept-Ranges', 'none')
print(f"Soporta peticiones de rango: {'Sí' if accept_ranges != 'none' else 'No'}")
else:
print(f"La petición HEAD falló con el código de estado: {head_response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
## Comprueba algunas URLs diferentes
check_url("https://httpbin.org/image/jpeg")
check_url("https://speed.hetzner.de/100MB.bin")
check_url("https://example.com")
Ejecuta este script para ver información detallada sobre los encabezados de respuesta:
python response_headers.py
Verás una salida que muestra los encabezados para diferentes tipos de contenido, incluyendo imágenes, archivos binarios y páginas HTML.
Comprender los encabezados de respuesta es crucial para muchas tareas de desarrollo web, como:
- Determinar los tipos y tamaños de archivos antes de descargarlos
- Implementar descargas reanudables con peticiones de rango
- Comprobar las políticas de caché y las fechas de caducidad
- Manejar redirecciones y autenticación
Resumen
En este laboratorio, has aprendido a trabajar con la biblioteca requests de Python para interactuar con servicios web y APIs. Ahora tienes las habilidades para:
- Realizar peticiones HTTP y manejar códigos de estado y errores de respuesta
- Analizar datos JSON de respuestas de API
- Extraer información de contenido HTML utilizando BeautifulSoup
- Descargar y procesar contenido binario como imágenes y archivos
- Trabajar con encabezados de respuesta y metadatos
Estas habilidades forman la base para muchas aplicaciones de Python, incluyendo web scraping, integración de API, recopilación de datos y automatización. Ahora puedes construir aplicaciones que interactúan con servicios web, extraen información útil de sitios web y procesan varios tipos de contenido web.
Para continuar aprendiendo, podrías explorar:
- Métodos de autenticación para acceder a APIs protegidas
- Trabajar con APIs más complejas que requieren encabezados específicos o formatos de petición
- Construir un proyecto completo de web scraping que recopile y analice datos
- Crear una aplicación Python que se integre con múltiples APIs
Recuerda que al realizar web scraping o usar APIs, es importante verificar los términos de servicio y respetar los límites de frecuencia (rate limits) para evitar ser bloqueado.



