El web scraping es la técnica de extraer información de páginas web de forma automática usando código.
Con Python puedes descargar el contenido HTML de cualquier página y luego buscar los datos que necesitas. Las bibliotecas requests y BeautifulSoup hacen este proceso sencillo y eficiente.
Instalación de las bibliotecas necesarias
Antes de escribir código, instala las dos bibliotecas principales.
pip install requests beautifulsoup4
requests descarga el contenido de la página. BeautifulSoup analiza el HTML y te permite buscar elementos específicos.
Estructura básica de un script de web scraping
Todo script de scraping sigue el mismo patrón de tres pasos.
Paso 1: Descargar el HTML con requests.get().
Paso 2: Parsear el HTML con BeautifulSoup.
Paso 3: Buscar y extraer los datos que necesitas.
import requests
from bs4 import BeautifulSoup
# Paso 1: Descargar la página
url = "https://ejemplo.com"
respuesta = requests.get(url)
# Paso 2: Parsear el HTML
sopa = BeautifulSoup(respuesta.text, "html.parser")
# Paso 3: Extraer datos
titulo = sopa.find("h1").text
print(titulo)
El argumento "html.parser" le dice a BeautifulSoup qué motor usar para leer el HTML. Es el parser estándar incluido en Python.
Ejemplo 1: Extraer el precio de un producto en Liverpool
Imagina que quieres monitorear el precio de un televisor en Liverpool. El HTML de la página del producto tiene una estructura similar a esta:
<div class="price">
<span class="current-price">$18,500</span>
</div>
Tu script de Python para extraer ese precio sería:
import requests
from bs4 import BeautifulSoup
url = "https://www.liverpool.com.mx/tienda/pdp/televisor-55/12345"
encabezados = {"User-Agent": "Mozilla/5.0"}
respuesta = requests.get(url, headers=encabezados)
sopa = BeautifulSoup(respuesta.text, "html.parser")
precio_tag = sopa.find("span", class_="current-price")
if precio_tag:
precio = precio_tag.text.strip()
print(f"Precio actual: {precio}")
else:
print("No se encontró el precio.")
Salida esperada:
Precio actual: $18,500
El parámetro headers con User-Agent es importante. Sin él, muchos sitios bloquean las solicitudes automáticas.
Ejemplo 2: Extraer múltiples productos de una lista
En Mercado Libre, los resultados de búsqueda muestran varios productos en una lista. Puedes usar find_all() para extraer todos los elementos de una sola vez.
import requests
from bs4 import BeautifulSoup
url = "https://listado.mercadolibre.com.mx/laptops"
encabezados = {"User-Agent": "Mozilla/5.0"}
respuesta = requests.get(url, headers=encabezados)
sopa = BeautifulSoup(respuesta.text, "html.parser")
productos = sopa.find_all("li", class_="ui-search-layout__item")
for producto in productos:
nombre_tag = producto.find("h2", class_="ui-search-item__title")
precio_tag = producto.find("span", class_="andes-money-amount__fraction")
nombre = nombre_tag.text.strip() if nombre_tag else "Sin nombre"
precio = f"${precio_tag.text.strip()}" if precio_tag else "Sin precio"
print(f"{nombre} — {precio}")
Salida esperada:
Laptop HP 15 Core i5 — $12,500
Laptop Lenovo IdeaPad 3 — $9,800
Laptop Dell Inspiron 15 — $14,200
find_all() regresa una lista con todos los elementos que coinciden con el selector. Luego iteras sobre esa lista para procesar cada uno.
Ejemplo 3: Guardar los datos en un archivo CSV
Extraer datos y mostrarlos en pantalla es útil. Guardarlos en un archivo CSV te permite analizarlos después con Excel o pandas.
import requests
from bs4 import BeautifulSoup
import csv
url = "https://listado.mercadolibre.com.mx/laptops"
encabezados = {"User-Agent": "Mozilla/5.0"}
respuesta = requests.get(url, headers=encabezados)
sopa = BeautifulSoup(respuesta.text, "html.parser")
productos = sopa.find_all("li", class_="ui-search-layout__item")
with open("productos.csv", "w", newline="", encoding="utf-8") as archivo:
escritor = csv.writer(archivo)
escritor.writerow(["Nombre", "Precio"])
for producto in productos:
nombre_tag = producto.find("h2", class_="ui-search-item__title")
precio_tag = producto.find("span", class_="andes-money-amount__fraction")
nombre = nombre_tag.text.strip() if nombre_tag else "Sin nombre"
precio = precio_tag.text.strip() if precio_tag else "0"
escritor.writerow([nombre, precio])
print("Datos guardados en productos.csv")
Este script crea un archivo productos.csv con dos columnas: Nombre y Precio. Puedes abrirlo directamente en Excel.
Selectores de BeautifulSoup: referencia rápida
| Método | Uso | Ejemplo |
|---|---|---|
find(tag) |
Primer elemento que coincide | sopa.find("h1") |
find_all(tag) |
Todos los elementos que coinciden | sopa.find_all("li") |
find(tag, class_=) |
Por etiqueta y clase CSS | sopa.find("span", class_="precio") |
find(id=) |
Por atributo ID | sopa.find(id="main-title") |
select(selector) |
Selector CSS completo | sopa.select(".lista > li") |
.text |
Texto visible del elemento | tag.text.strip() |
.get("href") |
Valor de un atributo | tag.get("href") |
El método select() acepta selectores CSS exactamente igual que en JavaScript. Es útil cuando los selectores son complejos.
Consideraciones legales y buenas prácticas
El web scraping tiene implicaciones legales que debes conocer antes de usarlo.
Revisa el archivo robots.txt: Toda página web tiene un archivo en https://sitio.com/robots.txt que indica qué rutas están permitidas para bots. Respetarlo es una buena práctica profesional.
Respeta los Términos de Servicio: Algunos sitios prohíben expresamente el scraping automatizado en sus TOS. Hacer scraping en esos sitios puede derivar en bloqueos de IP o problemas legales.
Agrega pausas entre solicitudes: Hacer cientos de solicitudes seguidas puede saturar el servidor del sitio. Usa time.sleep() para agregar un retraso entre cada solicitud.
import time
for pagina in range(1, 6):
url = f"https://ejemplo.com/productos?pagina={pagina}"
respuesta = requests.get(url, headers=encabezados)
# ... procesar datos ...
time.sleep(2) # Espera 2 segundos entre cada solicitud
Usa solo datos públicos: No extraigas información privada, datos personales o contenido detrás de un login sin autorización expresa.
Errores comunes
Error 1: Acceder a .text sin verificar si el elemento existe.
Si find() no encuentra el elemento, regresa None. Llamar .text sobre None genera un error AttributeError.
# Incorrecto
precio = sopa.find("span", class_="precio").text
# Correcto
precio_tag = sopa.find("span", class_="precio")
precio = precio_tag.text.strip() if precio_tag else "No disponible"
Siempre verifica que el tag no sea None antes de acceder a sus propiedades.
Error 2: No incluir el encabezado User-Agent.
Sin este encabezado, el servidor identifica tu script como un bot y puede bloquear la solicitud con un error 403. Siempre incluye headers={"User-Agent": "Mozilla/5.0"} en tus solicitudes.
Error 3: Asumir que el HTML no cambia.
Los sitios web actualizan su diseño y clases CSS con frecuencia. Un scraper que funciona hoy puede fallar mañana si el sitio cambia su estructura. Agrega manejo de errores con try/except para que tu script falle de forma controlada.
try:
precio = sopa.find("span", class_="precio").text.strip()
except AttributeError:
precio = "No encontrado"
print("Advertencia: la estructura del HTML cambió.")
Error 4: No manejar el código de respuesta HTTP.
Una solicitud puede fallar con errores 404 o 500. Verifica siempre que la respuesta fue exitosa antes de parsear.
respuesta = requests.get(url, headers=encabezados)
if respuesta.status_code == 200:
sopa = BeautifulSoup(respuesta.text, "html.parser")
else:
print(f"Error al acceder a la página: {respuesta.status_code}")
Puntos clave
requests.get()descarga el HTML yBeautifulSouplo analiza. Siempre incluyeheaders={"User-Agent": "Mozilla/5.0"}para evitar bloqueos.- Usa
find()para el primer elemento yfind_all()para obtener una lista completa de elementos coincidentes. - Verifica siempre que
find()no regreseNoneantes de acceder a.texto cualquier atributo del tag. - Agrega
time.sleep()entre solicitudes para no saturar el servidor y reducir el riesgo de ser bloqueado por IP. - Revisa el archivo
robots.txty los Términos de Servicio del sitio antes de hacer scraping. El uso responsable protege tu proyecto y evita consecuencias legales.