Convertir HTML a Markdown con Python: Una guía completa

Python para convertir HTML en Markdown limpio y listo para LLM

Índice

Convertir HTML a Markdown es una tarea fundamental en los flujos de trabajo de desarrollo modernos, especialmente cuando se prepara contenido web para Modelos de Lenguaje Grande (LLMs), sistemas de documentación o generadores de sitios estáticos como Hugo. Esta guía forma parte de nuestro Herramientas de Documentación en 2026: Markdown, LaTeX, PDF y Flujos de Trabajo de Impresión centro.

Mientras que el HTML está diseñado para navegadores web con riqueza de estilo y estructura, el Markdown ofrece un formato limpio y legible ideal para el procesamiento de texto, el control de versiones y la consumición por IA. Si eres nuevo en la sintaxis de Markdown, consulta nuestro Hoja de Referencia de Markdown para una referencia completa.

infografía: convertir página de html a markdown

En esta revisión completa, exploraremos seis paquetes de Python para la conversión de HTML a Markdown, proporcionando ejemplos prácticos de código, benchmarks de rendimiento y casos de uso reales. Ya estés construyendo un pipeline de entrenamiento de LLM, migrando un blog a Hugo o raspando documentación, encontrarás la herramienta perfecta para tu flujo de trabajo.

Enfoque Alternativo: Si necesitas una extracción de contenido más inteligente con comprensión semántica, también podrías considerar convertir HTML a Markdown usando LLM y Ollama, que ofrece una conversión impulsada por IA para diseños complejos.

Lo que aprenderás:

  • Comparación detallada de 6 bibliotecas con pros y contras de cada una
  • Benchmarks de rendimiento con muestras de HTML reales
  • Ejemplos de código listos para producción para casos de uso comunes
  • Mejores prácticas para flujos de trabajo de preprocesamiento de LLM
  • Recomendaciones específicas según tus requisitos

¿Por qué Markdown para el Preprocesamiento de LLM?

Antes de sumergirnos en las herramientas, entendamos por qué Markdown es especialmente valioso para flujos de trabajo de LLM:

  1. Eficiencia de Tokens: Markdown usa significativamente menos tokens que HTML para el mismo contenido
  2. Claridad Semántica: Markdown preserva la estructura del documento sin etiquetas verbosas
  3. Legibilidad: Tanto humanos como LLM pueden fácilmente analizar la sintaxis de Markdown
  4. Consistencia: El formato estandarizado reduce la ambigüedad en las entradas del modelo
  5. Almacenamiento: Tamaños de archivo más pequeños para datos de entrenamiento y ventanas de contexto

La versatilidad de Markdown va más allá de la conversión de HTML: también puedes convertir documentos de Word a Markdown para flujos de trabajo de documentación, o usarlo en sistemas de gestión de conocimiento como Obsidian para Gestión de Conocimiento Personal. Para más información sobre la conversión de documentos y formato entre Markdown, LaTeX y PDF, consulta el centro de herramientas de documentación.

TL;DR - Matriz de Comparación Rápida

Si estás apurado, aquí hay una comparación completa de las seis bibliotecas a primera vista. Esta tabla te ayudará a identificar rápidamente cuál herramienta coincide con tus requisitos específicos:

Característica html2text markdownify html-to-markdown trafilatura domscribe html2md
Soporte HTML5 Parcial Parcial Completo Completo Completo Completo
Tipos de Hint No No Parcial No Parcial
Manejadores Personalizados Limitado Excelente Bueno Limitado Bueno Limitado
Soporte de Tablas Básico Básico Avanzado Bueno Bueno Bueno
Soporte Asincrónico No No No No No
Extracción de Contenido No No No Excelente No Bueno
Extracción de Metadatos No No Excelente No
Herramienta CLI No No No
Velocidad Media Lenta Rápida Muy rápida Media Muy rápida
Desarrollo Activo No Limitado
Versión de Python 3.6+ 3.7+ 3.9+ 3.6+ 3.8+ 3.10+
Dependencias Ninguna BS4 lxml lxml BS4 aiohttp

Guía de Selección Rápida:

  • Necesitas velocidad? → trafilatura o html2md
  • Necesitas personalización? → markdownify
  • Necesitas seguridad de tipo? → html-to-markdown
  • Necesitas simplicidad? → html2text
  • Necesitas extracción de contenido? → trafilatura

Los Finalistas: 6 Paquetes de Python Comparados

Vamos a profundizar en cada biblioteca con ejemplos prácticos de código, opciones de configuración y insights del mundo real. Cada sección incluye instrucciones de instalación, patrones de uso y evaluaciones honestas de fortalezas y limitaciones.

1. html2text - La Elección Clásica

Originalmente desarrollado por Aaron Swartz, html2text ha sido un estándar en el ecosistema de Python durante más de una década. Se centra en producir salida de Markdown limpia y legible.

Instalación:

pip install html2text

Uso Básico:

import html2text

# Crear instancia del convertidor
h = html2text.HTML2Text()

# Configurar opciones
h.ignore_links = False
h.ignore_images = False
h.ignore_emphasis = False
h.body_width = 0  # No envolver líneas

html_content = """
<h1>Bienvenido al Web Scraping</h1>
<p>Este es un <strong>guía completa</strong> para extraer contenido.</p>
<ul>
    <li>Fácil de usar</li>
    <li>Bien probado</li>
    <li>Ampliamente adoptado</li>
</ul>
<a href="https://example.com">Aprender más</a>
"""

markdown = h.handle(html_content)
print(markdown)

Salida:

# Bienvenido al Web Scraping

Este es un **guía completa** para extraer contenido.

  * Fácil de usar
  * Bien probado
  * Ampliamente adoptado

[Aprender más](https://example.com)

Configuración Avanzada:

import html2text

h = html2text.HTML2Text()

# Saltar elementos específicos
h.ignore_links = True
h.ignore_images = True

# Controlar formato
h.body_width = 80  # Envolver a 80 caracteres
h.unicode_snob = True  # Usar caracteres unicode
h.emphasis_mark = '*'  # Usar * para énfasis en lugar de _
h.strong_mark = '**'

# Manejar tablas
h.ignore_tables = False

# Proteger texto pre-formateado
h.protect_links = True

Ventajas:

  • Maduro y estable (más de 15 años de desarrollo)
  • Opciones de configuración extensas
  • Maneja bien los casos límite
  • Sin dependencias externas

Desventajas:

  • Soporte limitado de HTML5
  • Puede producir espaciado inconsistente
  • No se mantiene activamente (última actualización mayor en 2020)
  • Solo procesamiento secuencial

Mejor Para: Documentos HTML simples, sistemas legados, cuando la estabilidad es primordial


2. markdownify - La Opción Flexible

markdownify aprovecha BeautifulSoup4 para proporcionar un análisis flexible de HTML con manejo personalizable de etiquetas.

Instalación:

pip install markdownify

Uso Básico:

from markdownify import markdownify as md

html = """
<article>
    <h2>Desarrollo Web Moderno</h2>
    <p>Construyendo con <code>Python</code> y <em>marcos modernos</em>.</p>
    <blockquote>
        <p>La simplicidad es la sofisticación ultimate.</p>
    </blockquote>
</article>
"""

markdown = md(html)
print(markdown)

Salida:


## Desarrollo Web Moderno

Construyendo con `Python` y *marcos modernos*.

> La simplicidad es la sofisticación ultimate.

Uso Avanzado con Manejadores Personalizados:

from markdownify import MarkdownConverter

class CustomConverter(MarkdownConverter):
    """
    Crear convertidor personalizado con manejo de etiquetas específico
    """
    def convert_img(self, el, text, convert_as_inline):
        """Manejador personalizado de imágenes con texto alternativo"""
        alt = el.get('alt', '')
        src = el.get('src', '')
        title = el.get('title', '')

        if title:
            return f'![{alt}]({src} "{title}")'
        return f'![{alt}]({src})'

    def convert_pre(self, el, text, convert_as_inline):
        """Manejo mejorado de bloques de código con detección de lenguaje"""
        code = el.find('code')
        if code:
            # Extraer lenguaje desde atributo de clase (ej. 'language-python')
            classes = code.get('class', [''])
            language = classes[0].replace('language-', '') if classes else ''
            return f'\n```{language}\n{code.get_text()}\n```\n'
        return f'\n```\n{text}\n```\n'

# Usar convertidor personalizado
html = '<pre><code class="language-python">def hello():\n    print("world")</code></pre>'
markdown = CustomConverter().convert(html)
print(markdown)

Para más detalles sobre el uso de bloques de código en Markdown y resaltado de sintaxis, consulta nuestra guía sobre Uso de Bloques de Código en Markdown.

Conversión Seleccionada de Etiquetas:

from markdownify import markdownify as md

# Eliminar etiquetas específicas por completo
markdown = md(html, strip=['script', 'style', 'nav'])

# Convertir solo etiquetas específicas
markdown = md(
    html,
    heading_style="ATX",  # Usar # para encabezados
    bullets="-",  # Usar - para viñetas
    strong_em_symbol="*",  # Usar * para énfasis
)

Ventajas:

  • Basado en BeautifulSoup4 (análisis de HTML robusto)
  • Muy personalizable mediante subclase
  • Mantenimiento activo
  • Buena documentación

Desventajas:

  • Requiere dependencia de BeautifulSoup4
  • Puede ser más lento para documentos grandes
  • Soporte limitado de tablas predefinido

Mejor Para: Lógica de conversión personalizada, proyectos que ya usan BeautifulSoup4


3. html-to-markdown - El Poderoso Moderno

html-to-markdown es una biblioteca completamente tipada, moderna con soporte completo de HTML5 y opciones de configuración extensas.

Instalación:

pip install html-to-markdown

Uso Básico:

from html_to_markdown import convert

html = """
<article>
    <h1>Documentación Técnica</h1>
    <table>
        <thead>
            <tr>
                <th>Característica</th>
                <th>Soporte</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>HTML5</td>
                <td>✓</td>
            </tr>
            <tr>
                <td>Tablas</td>
                <td>✓</td>
            </tr>
        </tbody>
    </table>
</article>
"""

markdown = convert(html)
print(markdown)

Configuración Avanzada:

from html_to_markdown import convert, Options

# Crear opciones personalizadas
options = Options(
    heading_style="ATX",
    bullet_style="-",
    code_language_default="python",
    strip_tags=["script", "style"],
    escape_special_chars=True,
    table_style="pipe",  # Usar | para tablas
    preserve_whitespace=False,
    extract_metadata=True,  # Extraer etiquetas meta
)

markdown = convert(html, options=options)

Interfaz de Línea de Comandos:

# Convertir un solo archivo
html-to-markdown input.html -o output.md

# Convertir con opciones
html-to-markdown input.html \
    --heading-style atx \
    --strip-tags script,style \
    --extract-metadata

# Conversión en lote
find ./html_files -name "*.html" -exec html-to-markdown {} -o ./markdown_files/{}.md \;

Ventajas:

  • Soporte completo de HTML5 incluyendo elementos semánticos
  • Seguro de tipo con pistas completas
  • Manejo mejorado de tablas (celdas fusionadas, alineación)
  • Capacidad de extracción de metadatos
  • Desarrollo activo y código base moderno

Desventajas:

  • Requiere Python 3.9+
  • Mayor huella de dependencia
  • Curva de aprendizaje más empinada

Mejor Para: Documentos HTML5 complejos, proyectos seguros de tipo, sistemas de producción


4. trafilatura - El Especialista en Extracción de Contenido

trafilatura no es solo un convertidor de HTML a Markdown, sino una biblioteca inteligente de extracción de contenido diseñada específicamente para el raspado web y la extracción de artículos.

Instalación:

pip install trafilatura

Uso Básico:

import trafilatura

# Descargar y extraer desde URL
url = "https://example.com/articulo"
descargado = trafilatura.fetch_url(url)
markdown = trafilatura.extract(descargado, output_format='markdown')
print(markdown)

Nota: Trafilatura incluye descarga integrada de URL, pero para operaciones HTTP más complejas, podrías encontrar útil nuestra Hoja de Referencia de cURL cuando trabajas con APIs o puntos finales autenticados.

Extracción Avanzada de Contenido:

import trafilatura
from trafilatura.settings import use_config

# Crear configuración personalizada
config = use_config()
config.set("DEFAULT", "EXTRACTION_TIMEOUT", "30")

html = """
<html>
<head><title>Título del Artículo</title></head>
<body>
    <nav>Menú de navegación</nav>
    <article>
        <h1>Artículo Principal</h1>
        <p>Contenido importante aquí.</p>
    </article>
    <aside>Publicidad</aside>
    <footer>Contenido del pie de página</footer>
</body>
</html>
"""

# Extraer solo contenido principal
markdown = trafilatura.extract(
    html,
    output_format='markdown',
    include_comments=False,
    include_tables=True,
    include_images=True,
    include_links=True,
    config=config
)

# Extraer con metadatos
resultado = trafilatura.extract(
    html,
    output_format='markdown',
    with_metadata=True
)

if resultado:
    print(f"Título: {resultado.get('title', 'N/A')}")
    print(f"Autor: {resultado.get('author', 'N/A')}")
    print(f"Fecha: {resultado.get('date', 'N/A')}")
    print(f"\nContenido:\n{resultado.get('text', '')}")

Procesamiento en Lote:

import trafilatura
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path

def process_url(url):
    """Extraer markdown desde URL"""
    downloaded = trafilatura.fetch_url(url)
    if downloaded:
        return trafilatura.extract(
            downloaded,
            output_format='markdown',
            include_links=True,
            include_images=True
        )
    return None

# Procesar múltiples URLs en paralelo
urls = [
    "https://example.com/articulo1",
    "https://example.com/articulo2",
    "https://example.com/articulo3",
]

with ThreadPoolExecutor(max_workers=5) as executor:
    resultados = list(executor.map(process_url, urls))

for i, markdown in enumerate(resultados):
    if markdown:
        Path(f"articulo_{i}.md").write_text(markdown, encoding='utf-8')

Ventajas:

  • Extracción inteligente de contenido (elimina elementos de plantilla)
  • Descarga integrada de URL con manejo robusto de errores
  • Extracción de metadatos (título, autor, fecha)
  • Detección de idioma
  • Optimizado para artículos de noticias y blogs
  • Análisis rápido basado en C

Desventajas:

  • Puede eliminar demasiado contenido para HTML general
  • Enfocado en extracción de artículos (no propósito general)
  • Complejidad de configuración para casos límite

Mejor Para: Raspado web, extracción de artículos, preparación de datos de entrenamiento de LLM


5. domscribe - El Preservador Semántico

domscribe se centra en preservar el significado semántico del HTML al convertirlo a Markdown.

Instalación:

pip install domscribe

Uso Básico:

from domscribe import html_to_markdown

html = """
<article>
    <header>
        <h1>Entendiendo HTML Semántico</h1>
        <time datetime="2024-10-24">24 de octubre de 2024</time>
    </header>
    <section>
        <h2>Introducción</h2>
        <p>El HTML semántico proporciona <mark>significado</mark> al contenido.</p>
    </section>
    <aside>
        <h3>Temas Relacionados</h3>
        <ul>
            <li>Accesibilidad</li>
            <li>SEO</li>
        </ul>
    </aside>
</article>
"""

markdown = html_to_markdown(html)
print(markdown)

Opciones Personalizadas:

from domscribe import html_to_markdown, MarkdownOptions

options = MarkdownOptions(
    preserve_semantic_structure=True,
    include_aria_labels=True,
    strip_empty_elements=True
)

markdown = html_to_markdown(html, options=options)

Ventajas:

  • Preserva la estructura semántica de HTML5
  • Maneja bien componentes web modernos
  • Diseño de API limpio

Desventajas:

  • Aún en desarrollo temprano (API puede cambiar)
  • Documentación limitada comparada con alternativas maduras
  • Comunidad más pequeña y menos ejemplos disponibles

Mejor Para: Documentos HTML5 semánticos, proyectos enfocados en accesibilidad, cuando la preservación de la estructura semántica de HTML5 es crítica

Nota: Aunque domscribe es más nuevo y menos probado que alternativas, llena un nicho específico para la preservación de HTML semántico que otras herramientas no priorizan.


6. html2md - El Poderoso Asincrónico

html2md está diseñado para conversiones de alto rendimiento en lotes con procesamiento asincrónico.

Instalación:

pip install html2md

Uso desde Línea de Comandos:

# Convertir directorio completo
m1f-html2md convert ./website -o ./docs

# Con configuraciones personalizadas
m1f-html2md convert ./website -o ./docs \
    --remove-tags nav,footer \
    --heading-offset 1 \
    --detect-language

# Convertir un solo archivo
m1f-html2md convert index.html -o readme.md

Uso Programático:

import asyncio
from html2md import convert_html

async def convert_files():
    """Conversión en lotes asincrónica"""
    html_files = [
        'page1.html',
        'page2.html',
        'page3.html'
    ]

    tasks = [convert_html(file) for file in html_files]
    results = await asyncio.gather(*tasks)
    return results

# Ejecutar conversión
resultados = asyncio.run(convert_files())

Ventajas:

  • Procesamiento asincrónico para alto rendimiento
  • Detección inteligente de selección de contenido
  • Generación de metadatos YAML (ideal para Hugo!)
  • Detección de lenguaje de código
  • Soporte de procesamiento paralelo

Desventajas:

  • Requiere Python 3.10+
  • CLI centrada (menos flexible API)
  • La documentación podría ser más completa

Mejor Para: Migraciones a gran escala, conversiones en lotes, migraciones de Hugo/Jekyll


Benchmarking de Rendimiento

El rendimiento importa, especialmente cuando se procesan miles de documentos para el entrenamiento de LLM o migraciones a gran escala. Entender las diferencias relativas de velocidad entre bibliotecas te ayuda a tomar decisiones informadas para tu flujo de trabajo.

Análisis Comparativo de Rendimiento:

Basado en patrones de uso típicos, aquí está cómo se comparan estas bibliotecas en tres escenarios realistas:

  1. HTML Simple: Post de blog básico con texto, encabezados y enlaces (5KB)
  2. HTML Complejo: Documentación técnica con tablas anidadas y bloques de código (50KB)
  3. Sitio Web Real: Página completa incluyendo navegación, pie de página, barra lateral y anuncios (200KB)

Aquí está el código de ejemplo de benchmark que puedes usar para probar estas bibliotecas por ti mismo:

import time
import html2text
from markdownify import markdownify
from html_to_markdown import convert
import trafilatura

def benchmark(html_content, iterations=100):
    """Benchmark de velocidad de conversión"""

    # html2text
    start = time.time()
    h = html2text.HTML2Text()
    for _ in range(iterations):
        _ = h.handle(html_content)
    html2text_time = time.time() - start

    # markdownify
    start = time.time()
    for _ in range(iterations):
        _ = markdownify(html_content)
    markdownify_time = time.time() - start

    # html-to-markdown
    start = time.time()
    for _ in range(iterations):
        _ = convert(html_content)
    html_to_markdown_time = time.time() - start

    # trafilatura
    start = time.time()
    for _ in range(iterations):
        _ = trafilatura.extract(html_content, output_format='markdown')
    trafilatura_time = time.time() - start

    return {
        'html2text': html2text_time,
        'markdownify': markdownify_time,
        'html-to-markdown': html_to_markdown_time,
        'trafilatura': trafilatura_time
    }

Características de Rendimiento Típicas (velocidades relativas representativas):

Paquete Simple (5KB) Complejo (50KB) Sitio Real (200KB)
html2text Moderado Más lento Más lento
markdownify Más lento Más lento Más lento
html-to-markdown Rápido Rápido Rápido
trafilatura Rápido Muy rápido Muy rápido
html2md (asincrónico) Muy rápido Muy rápido Más rápido

Observaciones Clave:

  • html2md y trafilatura son los más rápidos para documentos complejos, ideales para conversiones en lote
  • html-to-markdown ofrece el mejor equilibrio entre velocidad y características para uso en producción
  • markdownify es más lento pero más flexible—el intercambio vale la pena cuando necesitas manejadores personalizados
  • html2text muestra su edad con un rendimiento más lento, pero sigue siendo estable para casos de uso simples

Nota: Las diferencias de rendimiento se vuelven significativas solo cuando se procesan cientos o miles de archivos. Para conversiones ocasionales, cualquier biblioteca funcionará bien. Enfócate en características y opciones de personalización en su lugar.


Casos de Uso en el Mundo Real

La teoría es útil, pero ejemplos prácticos demuestran cómo funcionan estas herramientas en producción. Aquí hay cuatro escenarios comunes con código listo para producción que puedes adaptar para tus propios proyectos.

Caso de Uso 1: Preparación de Datos de Entrenamiento de LLM

Requisito: Extraer texto limpio de miles de páginas de documentación

Recomendado: trafilatura + procesamiento paralelo

import trafilatura
from pathlib import Path
from concurrent.futures import ProcessPoolExecutor

def process_html_file(html_path):
    """Convertir archivo HTML a markdown"""
    html = Path(html_path).read_text(encoding='utf-8')
    markdown = trafilatura.extract(
        html,
        output_format='markdown',
        include_links=False,  # Eliminar para datos de entrenamiento más limpios
        include_images=False,
        include_comments=False
    )

    if markdown:
        output_path = html_path.replace('.html', '.md')
        Path(output_path).write_text(markdown, encoding='utf-8')
        return len(markdown)
    return 0

# Procesar 10,000 archivos en paralelo
html_files = list(Path('./docs').rglob('*.html'))

with ProcessPoolExecutor(max_workers=8) as executor:
    token_counts = list(executor.map(process_html_file, html_files))

print(f"Procesados {len(html_files)} archivos")
print(f"Total de caracteres: {sum(token_counts):,}")

Caso de Uso 2: Migración de Blog de WordPress a Hugo

Requisito: Migrar un blog de WordPress a Hugo con metadatos

Recomendado: html2md CLI

Hugo es un popular generador de sitios estáticos que usa Markdown para contenido. Para más consejos específicos de Hugo, consulta nuestra Hoja de Referencia de Hugo y aprende sobre Agregar Marcado de Datos Estructurados a un Sitio Web de Hugo para mejor SEO. Nuestro centro de herramientas de documentación tiene más guías sobre flujos de trabajo de Markdown y conversión de documentos.

# Convertir todos los posts con metadatos
m1f-html2md convert ./exporta-wordpress \
    -o ./hugo/content/posts \
    --generate-frontmatter \
    --heading-offset 0 \
    --remove-tags script,style,nav,footer

O de forma programática:

from html_to_markdown import convert, Options
from pathlib import Path
import yaml

def migrate_post(html_file):
    """Convertir HTML de WordPress a Markdown de Hugo"""
    html = Path(html_file).read_text()

    # Extraer título y fecha desde HTML
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html, 'html.parser')
    title = soup.find('h1').get_text() if soup.find('h1') else 'Sin título'

    # Convertir a markdown
    options = Options(strip_tags=['script', 'style', 'nav', 'footer'])
    markdown = convert(html, options=options)

    # Añadir metadatos de Hugo
    frontmatter = {
        'title': title,
        'date': '2024-10-24',
        'draft': False,
        'tags': []
    }

    output = f"---\n{yaml.dump(frontmatter)}---\n\n{markdown}"

    # Guardar
    output_file = html_file.replace('.html', '.md')
    Path(output_file).write_text(output, encoding='utf-8')

# Procesar todos los posts
for html_file in Path('./exporta-wordpress').glob('*.html'):
    migrate_post(html_file)

Caso de Uso 3: Scraper de Documentación con Formateo Personalizado

Requisito: Raspado de documentación técnica con manejo personalizado de bloques de código

Recomendado: markdownify con convertidor personalizado

Este enfoque es especialmente útil para migrar documentación desde sistemas de wiki. Si estás gestionando documentación, también podrías estar interesado en DokuWiki - wiki autohospedado y alternativas para soluciones de documentación autohospedadas.

from markdownify import MarkdownConverter
import requests

class DocsConverter(MarkdownConverter):
    """Convertidor personalizado para documentación técnica"""

    def convert_pre(self, el, text, convert_as_inline):
        """Bloque de código mejorado con resaltado de sintaxis"""
        code = el.find('code')
        if code:
            # Extraer lenguaje desde clase
            classes = code.get('class', [])
            language = next(
                (c.replace('language-', '') for c in classes if c.startswith('language-')),
                'texto'
            )
            return f'\n```{language}\n{code.get_text()}\n```\n'
        return super().convert_pre(el, text, convert_as_inline)

    def convert_div(self, el, text, convert_as_inline):
        """Manejar bloques especiales de documentación"""
        classes = el.get('class', [])

        # Bloques de advertencia
        if 'warning' in classes:
            return f'\n> ⚠️ **Advertencia**: {text}\n'

        # Bloques de información
        if 'info' in classes or 'note' in classes:
            return f'\n> 💡 **Nota**: {text}\n'

        return text

def scrape_docs(url):
    """Raspar y convertir página de documentación"""
    response = requests.get(url)
    markdown = DocsConverter().convert(response.text)
    return markdown

# Usar
docs_url = "https://docs.example.com/api-reference"
markdown = scrape_docs(docs_url)
Path('api-reference.md').write_text(markdown)

Caso de Uso 4: Archivo de Newsletter a Markdown

Requisito: Convertir newsletters de HTML a markdown legible

Recomendado: html2text con configuración específica

import html2text
import email
from pathlib import Path

def convert_newsletter(email_file):
    """Convertir newsletter de HTML a markdown"""
    # Parsear correo
    with open(email_file, 'r') as f:
        msg = email.message_from_file(f)

    # Obtener parte HTML
    html_content = None
    for part in msg.walk():
        if part.get_content_type() == 'text/html':
            html_content = part.get_payload(decode=True).decode('utf-8')
            break

    if not html_content:
        return None

    # Configurar convertidor
    h = html2text.HTML2Text()
    h.ignore_images = False
    h.images_to_alt = True
    h.body_width = 0
    h.protect_links = True
    h.unicode_snob = True

    # Convertir
    markdown = h.handle(html_content)

    # Añadir metadatos
    subject = msg.get('Subject', 'Sin asunto')
    date = msg.get('Date', '')

    output = f"# {subject}\n\n*Fecha: {date}*\n\n---\n\n{markdown}"

    return output

# Procesar archivo de newsletter
for email_file in Path('./newsletters').glob('*.eml'):
    markdown = convert_newsletter(email_file)
    if markdown:
        output_file = email_file.with_suffix('.md')
        output_file.write_text(markdown, encoding='utf-8')

Recomendaciones por Escenario

¿Aún no estás seguro de qué biblioteca elegir? Aquí tienes una guía basada en casos de uso específicos.

Para Raspado Web y Preprocesamiento de LLM

Ganador: trafilatura

Trafilatura excela en extraer contenido limpio mientras elimina elementos de plantilla. Ideal para:

  • Construir conjuntos de datos de entrenamiento de LLM
  • Agregación de contenido
  • Colección de artículos de investigación
  • Extracción de artículos de noticias

Para Migraciones de Hugo/Jekyll

Ganador: html2md

El procesamiento asincrónico y la generación de metadatos hacen que las migraciones en masa sean rápidas y fáciles:

  • Conversiones en lote
  • Extracción automática de metadatos
  • Generación de metadatos YAML
  • Ajuste de nivel de encabezado

Para Lógica de Conversión Personalizada

Ganador: markdownify

Subclase el convertidor para un control completo:

  • Manejadores personalizados de etiquetas
  • Conversiones específicas del dominio
  • Requisitos de formato especiales
  • Integración con código existente de BeautifulSoup

Para Sistemas de Producción con Seguridad de Tipo

Ganador: html-to-markdown

Moderno, seguro de tipo y completo en características:

  • Soporte completo de HTML5
  • Pistas completas de tipo
  • Manejo avanzado de tablas
  • Mantenimiento activo

Para Conversiones Simples y Estables

Ganador: html2text

Cuando necesitas algo que “simplemente funciona”:

  • Sin dependencias
  • Probado y verificado
  • Opciones de configuración extensas
  • Soporte en múltiples plataformas

Mejores prácticas para el preprocesamiento de LLM

Independientemente de la biblioteca que elijas, seguir estas mejores prácticas garantizará una salida en Markdown de alta calidad optimizada para el consumo de LLM. Estos patrones han demostrado ser esenciales en flujos de trabajo de producción que procesan millones de documentos.

1. Limpieza antes de la conversión

Siempre elimina elementos no deseados antes de la conversión para obtener una salida más limpia y un mejor rendimiento:

from bs4 import BeautifulSoup
import trafilatura

def clean_and_convert(html):
    """Eliminar elementos no deseados antes de la conversión"""
    soup = BeautifulSoup(html, 'html.parser')

    # Eliminar elementos no deseados
    for element in soup(['script', 'style', 'nav', 'footer', 'header', 'aside']):
        element.decompose()

    # Eliminar anuncios y seguimiento
    for element in soup.find_all(class_=['ad', 'advertisement', 'tracking']):
        element.decompose()

    # Convertir HTML limpio
    markdown = trafilatura.extract(
        str(soup),
        output_format='markdown'
    )

    return markdown

2. Normalizar espacios en blanco

Diferentes convertidores manejan el espacio en blanco de manera diferente. Normaliza la salida para garantizar la consistencia en tu conjunto de datos:

import re

def normalize_markdown(markdown):
    """Limpiar el espaciado en markdown"""
    # Eliminar líneas en blanco múltiples
    markdown = re.sub(r'\n{3,}', '\n\n', markdown)

    # Eliminar espacio en blanco al final
    markdown = '\n'.join(line.rstrip() for line in markdown.split('\n'))

    # Asegurar una sola línea al final
    markdown = markdown.rstrip() + '\n'

    return markdown

3. Validar la salida

El control de calidad es esencial. Implementa validación para detectar errores de conversión a tiempo:

def validate_markdown(markdown):
    """Validar la calidad del markdown"""
    issues = []

    # Verificar restos de HTML
    if '<' in markdown and '>' in markdown:
        issues.append("Detectados etiquetas HTML")

    # Verificar enlaces rotos
    if '[' in markdown and ']()' in markdown:
        issues.append("Detectado enlace vacío")

    # Verificar bloques de código excesivos
    code_block_count = markdown.count('```')
    if code_block_count % 2 != 0:
        issues.append("Detectado bloque de código no cerrado")

    return len(issues) == 0, issues

4. Plantilla para procesamiento por lotes

Cuando se procesan grandes colecciones de documentos, usa esta plantilla lista para producción con manejo adecuado de errores, registro y procesamiento paralelo:

from pathlib import Path
from concurrent.futures import ProcessPoolExecutor
import trafilatura
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def process_file(html_path):
    """Procesar un solo archivo HTML"""
    try:
        html = Path(html_path).read_text(encoding='utf-8')
        markdown = trafilatura.extract(
            html,
            output_format='markdown',
            include_links=True,
            include_images=False
        )

        if markdown:
            # Normalizar
            markdown = normalize_markdown(markdown)

            # Validar
            is_valid, issues = validate_markdown(markdown)
            if not is_valid:
                logger.warning(f"{html_path}: {', '.join(issues)}")

            # Guardar
            output_path = Path(str(html_path).replace('.html', '.md'))
            output_path.write_text(markdown, encoding='utf-8')

            return True

        return False

    except Exception as e:
        logger.error(f"Error procesando {html_path}: {e}")
        return False

def batch_convert(input_dir, max_workers=4):
    """Convertir todos los archivos HTML en un directorio"""
    html_files = list(Path(input_dir).rglob('*.html'))
    logger.info(f"Encontrados {len(html_files)} archivos HTML")

    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        results = list(executor.map(process_file, html_files))

    success_count = sum(results)
    logger.info(f"Convertidos con éxito {success_count}/{len(html_files)} archivos")

# Uso
batch_convert('./html_docs', max_workers=8)

Conclusión

La ecosistema de Python ofrece herramientas maduras y listas para producción para la conversión de HTML a Markdown, cada una optimizada para diferentes escenarios. Tu elección debe alinearse con tus requisitos específicos:

  • Conversiones rápidas: Usa html2text por su simplicidad y ausencia de dependencias
  • Lógica personalizada: Usa markdownify para máxima flexibilidad mediante subclase
  • Escraping web: Usa trafilatura para extracción inteligente de contenido con eliminación de boilerplate
  • Migraciones masivas: Usa html2md para rendimiento asincrónico en proyectos a gran escala
  • Sistemas de producción: Usa html-to-markdown para seguridad de tipos y soporte completo de HTML5
  • Preservación semántica: Usa domscribe para mantener la estructura semántica de HTML5

Recomendaciones para flujos de trabajo de LLM

Para flujos de trabajo de preprocesamiento de LLM, se recomienda un enfoque de dos niveles:

  1. Comenzar con trafilatura para la extracción inicial de contenido — elimina inteligentemente navegación, anuncios y boilerplate mientras preserva el contenido principal
  2. Recurrir a html-to-markdown para documentos complejos que requieren preservación precisa de la estructura, como documentación técnica con tablas y bloques de código

Esta combinación maneja eficazmente el 95% de los escenarios reales.

Pasos siguientes

Para más guías sobre Markdown, LaTeX, procesamiento de PDF y flujos de trabajo de impresión de documentos, consulta Herramientas de documentación en 2026: Markdown, LaTeX, PDF y flujos de trabajo de impresión.

Todas estas herramientas (excepto html2text) están activamente manteniéndose y listas para producción. Es mejor:

  1. Instalar 2-3 bibliotecas que coincidan con tu caso de uso
  2. Probarlas con tus muestras HTML reales
  3. Benchmarkear el rendimiento con tamaños de documentos típicos
  4. Elegir basado en la calidad de la salida, no solo en la velocidad

El ecosistema de Python para la conversión de HTML a Markdown se ha madurado significativamente, y no te equivocarás con ninguna de estas opciones para sus casos de uso específicos.

Recursos adicionales

Nota: Esta comparación se basa en el análisis de la documentación oficial, retroalimentación de la comunidad y la arquitectura de las bibliotecas. Las características de rendimiento son representativas de los patrones de uso típicos. Para casos de uso específicos, realiza tus propios benchmarks con tus muestras HTML reales.

Otros artículos útiles