Tablas web con Python y Pandas: extracción de datos

Actualizado el: 21/08/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 contacto@example.com"}
)
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

    Búsqueda en PDF con Python y RAG
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Guía PDF con Python y RAG

    Aprende a consultar PDF con Python y RAG, recuperación semántica, evaluación y seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    23/07/2026
    Integração do ChatGPT com código Python em projeto prático
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    OpenAI API con Python: integración y seguridad

    Integra la API de OpenAI con Python usando Responses API, variables de entorno, validación, reintentos, control de costes, pruebas y

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Calendário mensal gerado automaticamente com Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Calendario mensual en Python con calendar

    Genera un calendario mensual con Python usando calendar y datetime, valida fechas, añade eventos y exporta a TXT, HTML o

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Projeto de jogo da velha desenvolvido em Python como exercício de lógica de programação
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Tres en raya con Python: lógica y validación

    Crea un juego de tres en raya con Python usando listas, funciones, validación, detección de victoria, marcador, pruebas y una

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    texto Tkinter com uma interface gráfica dele ao lado
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Tkinter en Python: crea una app de escritorio

    Crea tu primera aplicación con Tkinter: ventanas, ttk, formularios, grid, eventos, archivos, threads, estilos y separación de lógica.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Sistema de lembretes e alarmes desenvolvido com Python
    Proyectos
    Foto de perfil de Leandro Hirt da Academify

    Recordatorios con alarma en Python: datetime y sonido

    Crea recordatorios con alarma en Python usando datetime, JSON, threads, zonas horarias, sonidos, recurrencia y pruebas controladas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026