Anatomía de las peticiones web
Para capturar datos renderizados por el cliente, se pueden usar herramientas de captura de paquetes:
F12 (o clic derecho → Inspeccionar) → Network → aquí se pueden encontrar tanto la estrutcura como los datos. Usa Headers y Preview para examinarlos.
Protocolo HTTP
Un protocolo es un acuerdo formal entre dos computadores para la transmisión de datos. HTTP (HyperText Transfer Protocol) se utiliza para transferir HTML y otros contenidos hipermediales.
Componentes principales:
1. Petición (Request):
- Línea de petición: método (GET/POST), URL, protocolo
- Encabezados: información adicional requerida por el servidor (usado para anti-scraping)
- Cuerpo: parámetros de la petición (como términos de búsqueda)
2. Respuesta (Response):
- Línea de estado: protocolo, código de estado (200, 404, etc.)
- Encabezados: información adicional para el cliente
- Cuerpo: contenido real devuelto (HTML, JSON, etc.)
Encabezados de petición importantes para scraping:
- User-Agent: identificador del cliente (navegador, móvil, desktop)
- Referer: prevención de hotlinking (de dónde proviene la petición)
- Cookie: datos de sesión del usuario (tokens, info de login)
Métodos de petición:
- GET: envío visible de datos, para consultas
- POST: envío oculto, para modificar datos del servidor o subir archivos
Librería Requests
Requests permite a Python enviar peticiones HTTP fácilmente y interactuar con servidores web. Se puede usar para obtener contenido de páginas, enviar formularios, descargar archivos, consumir APIs, etc.
Métodos principales
respuesta = requests.get(direccion_url, headers=cabeceras, verify=True/False)
Realiza una petición GET y devuelve un objeto de respuesta. Sus propiedades más útiles son:
| Propiedad | Descripción |
|---|---|
| encoding | Consultar o establecer la codificación de caracteres |
| status_code | Código HTTP de respuesta |
| url | URL consultada |
| headers | Encabezados de respuesta |
| cookies | Información de cookies |
| text | Contenido como cadena de texto (código fuente HTML) |
| content | Contenido como bytes (para descargar imágenes/archivos) |
Peticiones GET
Para evitar bloqueos básicos, se debe simular ser un navegador:
import requests
cabeceras = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
enlace = "https://www.ejemplo.com"
resultado = requests.get(enlace, headers=cabeceras)
print(resultado.status_code)
print(resultado.text)
Peticiones POST
Por ejemplo, para consultar un servicio de traducción:
import requests
palabra = input("Introduce una palabra: ")
datos_form = {"query": palabra}
url_api = "https://api.ejemplo-traduccion.com/translate"
cabeceros = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}
respuesta = requests.post(url_api, data=datos_form, headers=cabeceros)
print(respuesta.json())
respuesta.close()
Es importante cerrar la respuesta con respuesta.close() para evitar problemas cuando se realizan muchas peticiones.
Parámetros en URL (Query String)
En peticiones GET, los parámetros se envían en la URL después de ?. Requests permite encapsularlos en un diccionario:
import requests
parametros_busqueda = {
"categoria": "comedia",
"rango": "100:90",
"accion": "",
"inicio": 0,
"limite": 20
}
cabeceros = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}
url_base = "https://api.ejemplo-peliculas.com/lista"
respuesta = requests.get(url_base, params=parametros_busqueda, headers=cabeceros)
print(respuesta.json())
Codificación de caracteres y problemas de codificación
Python utiliza Unicode internamente, pero para almacenamiento y transmisión se necesita codificar (UTF-8, GBK, etc.). El charset por defecto depende del sistema operativo: macOS usa UTF-8, Windows usa GBK.
Es crucial revisar el charset del sitio web (en los encabezados) y especificar el mismo al abrir archivos:
with open("datos.html", "w", encoding="utf-8") as f:
f.write(respuesta.text)
Análisis de datos
Expresiones regulares (Re)
Las expresiones regulares permiten buscar patrones en cadenas de texto.
Metacaracteres básicos:
.- Cualquier carácter excepto salto de línea\w- Letras, números, guion bajo\s- Espacios en blanco\d- Dígitos^- Inicio de cadena$- Fin de cadena[...]- Conjunto de caracteres[^...]- Negación del conjunto
Cuantificadores:
*- 0 o más veces+- 1 o más veces?- 0 o 1 vez{n}- Exactamente n veces{n,}- n o más veces{n,m}- Entre n y m veces
Modo greedy vs lazy:
.*- Greedy: captura la cadena más larga posible.*?- Lazy: captura la cadena más corta posible
Módulo re de Python:
import re
# findall: devuelve lista con todas las coincidencias
coincidencias = re.findall(r"\d+", "Mi teléfono es 5551234, el suyo 9876543")
# finditer: devuelve iterador de objetos Match
iterador_matches = re.finditer(r"\d+", "Códigos: 100, 200, 300")
for match in iterador_matches:
print(match.group())
# search: encuentra la primera coincidencia
primer_match = re.search(r"\d+", "Hay 42 gatos y 7 perros")
if primer_match:
print(primer_match.group())
# match: debe coincidir desde el inicio
match_inicio = re.match(r"\d+", "12345 empieza aquí")
if match_inicio:
print(match_inicio.group())
# compile: precompila el patrón para reutilizarlo
patron = re.compile(r"\d{4}")
resultados = patron.finditer("Años: 2020, 2021, 2022")
for r in resultados:
print(r.group())
BeautifulSoup4 (bs4)
BeautifulSoup analiza documentos HTML y XML, creando un árbol de elementos navegable.
Sintaxis HTML: <etiqueta atributo="valor">contenido</etiqueta>
Un elemento puede tener múltiples atributos, lo que permite localizar contenido específico.
Ejemplo de uso:
from bs4 import BeautifulSoup
import requests
cabeceros = {"User-Agent": "Mozilla/5.0"}
respuesta = requests.get("https://ejemplo.com", headers=cabeceros)
sopa = BeautifulSoup(respuesta.text, "html.parser")
# Encontrar por etiqueta
titulos = sopa.find_all("h2")
for titulo in titulos:
print(titulo.text)
# Encontrar por clase CSS
articulos = sopa.find_all("div", class_="producto")
for articulo in articulos:
nombre = articulo.find("span", class_="nombre").text
precio = articulo.find("span", class_="precio").text
print(f"{nombre}: {precio}")
XPath
XPath es un lenguaje para navegar y buscar contenido en documentos XML. Como HTML es un subconjunto de XML, XPath funciona también para HTML.
En XML, cada etiqueta es un nodo. Los nodos pueden ser: padre, hijo, o hermano según su jerarquía.
Instalación de lxml:
pip install lxml
Sintaxis básica de XPath:
/- Selecciona desde el nodo raíz//- Selecciona nodos en cualquier posición@- Selecciona atributos[ ]- Predicados para filtrar
Ejemplo con lxml:
from lxml import etree
import requests
cabeceros = {"User-Agent": "Mozilla/5.0"}
respuesta = requests.get("https://ejemplo.com", headers=cabeceros)
arbol = etree.HTML(respuesta.text)
# Seleccionar todos los enlaces
enlaces = arbol.xpath("//a/@href")
for enlace in enlaces:
print(enlace)
# Seleccionar texto dentro de un div con clase específica
titulos = arbol.xpath('//div[@class="titulo"]/text()')
for titulo in titulos:
print(titulo.strip())
# Usar predicados para filtrar
primer_parrafo = arbol.xpath("//p[1]/text()")
print(primer_parrafo)
Requests avanzado
Manejo de Cookies y Sesiones
Para sitios que requieren autenticación, es necesario gestionar cookies. Una sesión mantiene las cookies entre múltiples peticiones:
import requests
# Crear sesión que mantiene cookies automáticamente
sesion = requests.Session()
cabeceros = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
}
# 1. Login: obtener cookie de sesión
datos_login = {
"usuario": "mi_usuario",
"clave": "mi_password"
}
respuesta_login = sesion.post("https://ejemplo.com/login", data=datos_login, headers=cabeceros)
# 2. La cookie se mantiene automáticamente en la sesión
respuesta_datos = sesion.get("https://ejemplo.com/perfil", headers=cabeceros)
print(respuesta_datos.text)
sesion.close()
Prevención de hotlinking (Referer)
Algunos sitios verifican de dónde proviene la petición usando el encabezado Referer:
cabeceros = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
"Referer": "https://ejemplo.com/pagina-origen"
}
respuesta = requests.get("https://ejemplo.com/video.mp4", headers=cabeceros)
with open("video.mp4", "wb") as f:
f.write(respuesta.content)
Uso de proxies
Para evitar bloqueos de IP:
proxies = {
"http": "http://usuario:password@proxy.ejemplo.com:8080",
"https": "https://usuario:password@proxy.ejemplo.com:8080"
}
respuesta = requests.get("https://ejemplo.com", proxies=proxies, headers=cabeceros)
print(respuesta.text)