Cómo extraer tablas de sitios web con Python y Pandas

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Extração de dados de tabelas de sites usando Python e pandas

Muchos sitios publican precios, clasificaciones, calendarios, indicadores y catálogos dentro de tablas HTML. Copiar esos datos manualmente es lento y propenso a errores. Python y Pandas pueden detectar tablas, convertirlas en DataFrames, limpiar columnas y exportar los resultados a CSV o Excel. Sin embargo, no todas las páginas utilizan tablas HTML reales, y cualquier extracción debe respetar los términos del sitio, la privacidad y los límites de acceso.

Para seguir esta guía conviene conocer Pandas para principiantes, la biblioteca Requests en Python, el manejo de archivos CSV y los fundamentos de try y except. La extracción confiable combina descarga, validación, limpieza y pruebas.

Instalar las dependencias

python -m venv .venv
# Windows
.venv\Scripts\activate
# Linux o macOS
source .venv/bin/activate

python -m pip install pandas lxml requests beautifulsoup4 openpyxl

pandas.read_html() puede utilizar analizadores como lxml. openpyxl es útil para exportar a Excel.

Leer todas las tablas de una página

import pandas as pd

url = "https://example.com/datos"
tablas = pd.read_html(url)

print(f"Tablas encontradas: {len(tablas)}")
for indice, tabla in enumerate(tablas):
    print(indice, tabla.shape)

read_html() devuelve una lista de DataFrames. Cada elemento corresponde a una tabla detectada. La documentación oficial de pandas.read_html explica parámetros como match, attrs, header y decimal.

Elegir una tabla concreta

No dependas únicamente de su posición, porque el sitio puede insertar otra tabla antes. Busca texto relevante:

tablas = pd.read_html(url, match="Población")
df = tablas[0]

También puedes filtrar por atributos HTML:

tablas = pd.read_html(
    url,
    attrs={"id": "tabla-principal"}
)

Inspecciona el código de la página para identificar un id o una clase estable.

Descargar primero con Requests

Para controlar encabezados, tiempo de espera y errores, descarga la página antes de analizarla:

import requests
from io import StringIO

respuesta = requests.get(
    url,
    timeout=20,
    headers={"User-Agent": "MiAnalizador/1.0 [email protected]"}
)
respuesta.raise_for_status()

tablas = pd.read_html(StringIO(respuesta.text))

Un identificador claro ayuda a los administradores a reconocer el tráfico. No finjas ser un navegador para evadir restricciones.

Limpiar encabezados de varias filas

Algunas tablas usan encabezados jerárquicos y producen columnas MultiIndex:

df.columns = [
    "_".join(str(parte) for parte in columna if "Unnamed" not in str(parte))
    if isinstance(columna, tuple)
    else str(columna)
    for columna in df.columns
]

Después normaliza:

df.columns = (
    pd.Index(df.columns)
      .str.strip()
      .str.lower()
      .str.replace(" ", "_", regex=False)
)

Eliminar filas repetidas de encabezado

Las tablas largas pueden repetir el encabezado dentro del cuerpo:

df = df[df["pais"] != "País"]

La regla debe ser específica para no eliminar una fila legítima.

Convertir números

df["poblacion"] = (
    df["poblacion"]
      .astype("string")
      .str.replace(".", "", regex=False)
      .str.replace(",", ".", regex=False)
)

df["poblacion"] = pd.to_numeric(
    df["poblacion"],
    errors="coerce"
)

Los separadores varían según el idioma. En algunos casos puedes utilizar los parámetros thousands y decimal durante la lectura.

Eliminar notas y símbolos

df["valor"] = (
    df["valor"]
      .astype("string")
      .str.replace(r"\[\d+\]", "", regex=True)
      .str.replace("%", "", regex=False)
      .str.strip()
)

Conserva el valor original en otra columna cuando los símbolos tengan significado.

Extraer enlaces junto con el texto

read_html() se concentra en el contenido visible. Si necesitas enlaces, utiliza Beautiful Soup. La documentación oficial de Beautiful Soup describe selectores y navegación del árbol HTML.

from bs4 import BeautifulSoup

sopa = BeautifulSoup(respuesta.text, "html.parser")
tabla_html = sopa.select_one("#tabla-principal")

for fila in tabla_html.select("tr"):
    enlace = fila.select_one("a[href]")
    if enlace:
        print(enlace.get_text(strip=True), enlace.get("href"))

Combinar Pandas y Beautiful Soup

Una estrategia útil es utilizar Pandas para los valores y Beautiful Soup para metadatos no incluidos en las celdas. Asegúrate de que el orden de las filas coincida antes de unir resultados.

Procesar varias páginas

resultados = []

for pagina in range(1, 6):
    url_pagina = f"https://example.com/datos?page={pagina}"
    respuesta = requests.get(url_pagina, timeout=20)
    respuesta.raise_for_status()
    tabla = pd.read_html(StringIO(respuesta.text))[0]
    tabla["pagina_origen"] = pagina
    resultados.append(tabla)

completo = pd.concat(resultados, ignore_index=True)

Añade pausas y respeta los límites publicados. Una descarga masiva puede afectar al servidor.

Evitar duplicados entre páginas

completo = completo.drop_duplicates(
    subset=["id"],
    keep="last"
)

Si no existe un identificador, crea una clave con varias columnas. No elimines duplicados basándote solo en el nombre cuando pueden existir entidades distintas.

Exportar a CSV y Excel

completo.to_csv(
    "datos_web.csv",
    index=False,
    encoding="utf-8"
)

completo.to_excel(
    "datos_web.xlsx",
    index=False,
    sheet_name="Datos"
)

Incluye columnas con URL y fecha de extracción para mantener trazabilidad.

Guardar una copia del HTML

from datetime import datetime
from pathlib import Path

marca = datetime.now().strftime("%Y%m%d_%H%M%S")
Path(f"capturas/pagina_{marca}.html").write_text(
    respuesta.text,
    encoding="utf-8"
)

Guardar el HTML ayuda a reproducir errores si el sitio cambia. Crea la carpeta previamente y considera el espacio de almacenamiento.

Páginas renderizadas con JavaScript

Si el HTML descargado no contiene la tabla, los datos pueden cargarse mediante JavaScript. Antes de usar un navegador automatizado, abre las herramientas de desarrollo y busca una solicitud JSON pública. Consumir un endpoint documentado suele ser más estable y eficiente que controlar un navegador.

Cuando sea imprescindible, herramientas como Selenium o Playwright pueden esperar a que la tabla aparezca. Esto consume más recursos y requiere más mantenimiento.

Detectar cambios de estructura

columnas_esperadas = {"pais", "valor", "fecha"}
actuales = set(df.columns)

if not columnas_esperadas.issubset(actuales):
    raise ValueError(
        f"La estructura cambió. Columnas: {sorted(actuales)}"
    )

Estas validaciones evitan exportar silenciosamente datos incorrectos cuando el sitio modifica encabezados.

Reintentos y errores temporales

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

sesion = requests.Session()
reintentos = Retry(
    total=3,
    backoff_factor=1,
    status_forcelist=[429, 500, 502, 503, 504]
)
sesion.mount("https://", HTTPAdapter(max_retries=reintentos))

No repitas solicitudes indefinidamente. El código 429 indica que debes reducir la frecuencia.

Aspectos éticos y legales

Consulta los términos de uso, robots.txt y políticas de la fuente. No extraigas datos personales, contenido protegido o información detrás de autenticación sin permiso. Identifica tu aplicación, limita la velocidad y utiliza APIs oficiales cuando existan.

Errores frecuentes

Los fallos más comunes son asumir que la primera tabla siempre es la correcta, ignorar separadores numéricos, depender de clases CSS inestables, no establecer timeout, descargar demasiado rápido y no validar columnas. También es común confundir una tabla visual construida con div con una verdadera etiqueta table.

Conclusión

pandas.read_html() permite convertir tablas HTML en DataFrames con muy poco código. Requests añade control sobre la descarga y Beautiful Soup ayuda a recuperar enlaces y elementos específicos. Un extractor confiable valida la estructura, respeta límites, conserva la fuente y registra la fecha. Así puedes convertir tablas públicas en datos limpios y reproducibles sin depender de copiar y pegar manualmente.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Landing page simples criada com Python e Tailwind CSS
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear una landing page con Python y Tailwind CSS

    Crea una landing page con Flask y Tailwind CSS: diseño responsivo, formularios, validación, SQLite, seguridad, SEO y despliegue.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Integração de bots Discord com Python para automação
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un bot de Discord con Python

    Crea un bot de Discord con Python y discord.py: comandos, slash commands, permisos, intents, Cogs, tareas, logs y seguridad del

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Conversão de áudio para texto usando Python gratuitamente
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Cómo convertir audio a texto con Python gratis

    Convierte audio a texto con Python usando Whisper y SpeechRecognition, genera TXT y SRT, procesa carpetas y mejora privacidad y

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Sistema de tradução automática desenvolvido com Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un sistema de traducción automática con Python

    Crea un sistema de traducción automática con Python, APIs oficiales, variables de entorno, lotes, caché, reintentos y protección de marcadores.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Gerenciador de senhas simples desenvolvido com Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Cómo crear un gestor de contraseñas sencillo con Python

    Crea un gestor de contraseñas con Python, Fernet, secrets, JSON, getpass, derivación de claves, copias de seguridad y prácticas seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Geração automática de QR Code usando Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Cómo generar códigos QR con Python paso a paso

    Genera códigos QR con Python y qrcode: tamaños, corrección de errores, colores, logotipos, lotes CSV, rutas seguras y validación.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026