Convertir HTML a Markdown con Python: Una guía completa
Python para convertir HTML en Markdown limpio y listo para LLM
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.
Aunque el HTML está diseñado para navegadores web con riqueza de estilo y estructura, el Markdown ofrece un formato limpio y legible que es ideal para el procesamiento de texto, el control de versiones y la consumición por parte de IA. Si eres nuevo en la sintaxis de Markdown, consulta nuestro Markdown Cheatsheet para una referencia completa.

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 reales de HTML
- 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:
- Eficiencia de tokens: Markdown utiliza significativamente menos tokens que HTML para el mismo contenido
- Claridad semántica: Markdown preserva la estructura del documento sin etiquetas verbosas
- Legibilidad: Tanto humanos como LLM pueden analizar fácilmente la sintaxis de Markdown
- Consistencia: Un formato estandarizado reduce la ambigüedad en las entradas del modelo
- 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.
TL;DR - Matriz de comparación rápida
Si estás apurado, aquí hay una comparación completa de todas las seis bibliotecas a primera vista. Esta tabla te ayudará a identificar rápidamente qué 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 datos | No | No | Sí | Parcial | No | Parcial |
| Manejadores personalizados | Limitado | Excelente | Bueno | Limitado | Bueno | Limitado |
| Soporte de tablas | Básico | Básico | Avanzado | Bueno | Bueno | Bueno |
| Soporte asíncrono | No | No | No | No | No | Sí |
| Extracción de contenido | No | No | No | Excelente | No | Bueno |
| Extracción de metadatos | No | No | Sí | Excelente | No | Sí |
| Herramienta CLI | No | No | Sí | Sí | No | Sí |
| Velocidad | Media | Lenta | Rápida | Muy rápida | Media | Muy rápida |
| Desarrollo activo | No | Sí | Sí | Sí | Limitado | Sí |
| 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 rápida de selección:
- Necesitas velocidad? → trafilatura o html2md
- Necesitas personalización? → markdownify
- Necesitas seguridad de tipos? → html-to-markdown
- Necesitas simplicidad? → html2text
- Necesitas extracción de contenido? → trafilatura
Los contendientes: 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 opción clásica
Originalmente desarrollado por Aaron Swartz, html2text ha sido un pilar 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 el formato
h.body_width = 80 # Envolver en 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:
- Madura y estable (más de 15 años de desarrollo)
- Opciones de configuración extensas
- Maneja bien 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 en un solo hilo
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''
return f''
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 de 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 Markdown y resaltado de sintaxis, consulta nuestra guía sobre Uso de bloques de código Markdown.
Conversión selectiva de etiquetas:
from markdownify import markdownify as md
# Eliminar completamente etiquetas específicas
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 robusto de HTML)
- 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 por lotes
find ./html_files -name "*.html" -exec html-to-markdown {} -o ./markdown_files/{}.md \;
Ventajas:
- Soporte completo de HTML5 incluyendo elementos semánticos
- Seguro de tipos 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+
- Huella de dependencia más grande
- Curva de aprendizaje más pronunciada
Mejor para: Documentos HTML5 complejos, proyectos seguros de tipos, sistemas de producción
4. trafilatura - El especialista en extracción de contenido
trafilatura no es solo una herramienta de conversión de HTML a Markdown, sino una biblioteca inteligente de extracción de contenido especialmente diseñada para el raspado web y la extracción de artículos.
Instalación:
pip install trafilatura
Uso básico:
import trafilatura
# Descargar e 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 URLs, pero para operaciones HTTP más complejas, podrías encontrar útil nuestra Guía de comandos cURL al trabajar 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 por lotes:
import trafilatura
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
def process_url(url):
"""Extraer markdown desde URL"""
descargado = trafilatura.fetch_url(url)
if descargado:
return trafilatura.extract(
descargado,
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 boilerplate)
- Descarga integrada de URLs 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
- Parseo rápido basado en C
Desventajas:
- Puede eliminar demasiado contenido para HTML general
- Enfocado en extracción de artículos (no general)
- Complejidad de configuración para casos límite
Mejor para: Raspado web, extracción de artículos, preparación de datos de entrenamiento para 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 modernos de la web
- Diseño de API limpio
Desventajas:
- Aún en desarrollo temprano (API puede cambiar)
- Documentación limitada en comparación 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 una necesidad específica para la preservación de la estructura semántica de HTML que otras herramientas no priorizan.
6. html2md - El poderoso asíncrono
html2md está diseñado para conversiones de alto rendimiento en lotes con procesamiento asíncrono.
Instalación:
pip install html2md
Uso desde línea de comandos:
# Convertir directorio completo
m1f-html2md convert ./website -o ./docs
# Con configuración personalizada
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 asíncrona por lotes"""
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 asíncrono para alto rendimiento
- Detección inteligente de selección de contenido
- Generación de YAML frontmatter (ideal para Hugo!)
- Detección de lenguaje de código
- Soporte de procesamiento paralelo
Desventajas:
- Requiere Python 3.10+
- Enfoque en CLI (menos flexible API)
- Documentación podría ser más completa
Mejor para: Migraciones a gran escala, conversiones por 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 típicos de uso, aquí está cómo se comparan estas bibliotecas en tres escenarios realistas:
- HTML simple: Post de blog básico con texto, encabezados y enlaces (5KB)
- HTML complejo: Documentación técnica con tablas anidadas y bloques de código (50KB)
- Sitio web real: Página completa incluyendo navegación, pie de página, barra lateral y anuncios (200KB)
Aquí hay un ejemplo de código 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 | Moderada | Más lenta | Más lenta |
| markdownify | Más lenta | Más lenta | Más lenta |
| html-to-markdown | Rápida | Rápida | Rápida |
| trafilatura | Rápida | Muy rápida | Muy rápida |
| html2md (asíncrono) | Muy rápida | Muy rápida | Más rápida |
Observaciones clave:
html2mdytrafilaturason los más rápidos para documentos complejos, lo que los hace ideales para procesamiento por loteshtml-to-markdownofrece el mejor equilibrio entre velocidad y características para uso en producciónmarkdownifyes más lenta pero más flexible—el intercambio vale la pena cuando necesitas manejadores personalizadoshtml2textmuestra 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 reales
La teoría es útil, pero los ejemplos prácticos demuestran cómo funcionan estas herramientas en producción. Aquí hay cuatro escenarios comunes con código completo y listo para producción que puedes adaptar para tus propios proyectos.
Caso de uso 1: Preparación de datos de entrenamiento para 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"Caracteres totales: {sum(token_counts):,}")
Caso de uso 2: Migración de blog de Hugo
Requisito: Migrar blog de WordPress a Hugo con frontmatter
Recomendado: html2md CLI
Hugo es un popular generador de sitios estáticos que utiliza Markdown para el contenido. Para más consejos específicos de Hugo, consulta nuestra Hoja de trucos de Hugo y aprende sobre Agregar marcado de datos estructurados a Hugo para mejorar el SEO.
# Convertir todos los posts con frontmatter
m1f-html2md convert ./wordpress-export \
-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 del 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 frontmatter 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('./wordpress-export').glob('*.html'):
migrate_post(html_file)
Caso de uso 3: Scraper de documentación con formato personalizado
Requisito: Extraer 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 la clase
classes = code.get('class', [])
language = next(
(c.replace('language-', '') for c in classes if c.startswith('language-')),
'text'
)
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):
"""Extraer 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 boletines en formato Markdown
Requisito: Convertir boletines en formato 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 boletín HTML a markdown"""
# Parsear correo
with open(email_file, 'r') as f:
msg = email.message_from_file(f)
# Obtener contenido 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 boletines
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 mi guía definitiva basada en casos de uso específicos. Estas recomendaciones provienen de la experiencia práctica con cada biblioteca en entornos de producción.
Para scraping web y preprocesamiento de LLM
Ganador: trafilatura
Trafilatura destaca por extraer contenido limpio mientras elimina elementos innecesarios. Ideal para:
- Crear conjuntos de datos de entrenamiento para LLM
- Agregación de contenido
- Recolecció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 frontmatter hacen que las migraciones masivas sean rápidas y fáciles:
- Conversiones por lotes
- Extracción automática de metadatos
- Generación de frontmatter en YAML
- Ajuste de niveles de encabezado
Para lógica de conversión personalizada
Ganador: markdownify
Subclase el convertidor para tener completo control:
- Manejadores personalizados de etiquetas
- Conversiones específicas del dominio
- Requisitos de formato especial
- Integración con código existente de BeautifulSoup
Para sistemas de producción con tipos seguros
Ganador: html-to-markdown
Moderno, con tipos seguros y completo en características:
- Soporte completo para HTML5
- Indicaciones de tipo completas
- Manejo avanzado de tablas
- Mantenimiento activo
Para conversiones simples y estables
Ganador: html2text
Cuando necesitas algo que “funcione”:
- Sin dependencias
- Probado en batallas
- Configuración extensa
- Soporte en múltiples plataformas
Buenas prácticas para preprocesamiento de LLM
Independientemente de la biblioteca que elijas, seguir estas buenas prácticas garantizará una salida de Markdown de alta calidad optimizada para el consumo por parte de LLM. Estos patrones han demostrado ser esenciales en flujos de trabajo de producción que procesan millones de documentos.
1. Limpiar antes de convertir
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 los espacios en blanco de manera diferente. Normaliza la salida para garantizar coherencia en tu corpus:
import re
def normalize_markdown(markdown):
"""Limpiar espaciado en markdown"""
# Eliminar múltiples líneas en blanco
markdown = re.sub(r'\n{3,}', '\n\n', markdown)
# Eliminar espacio en blanco final
markdown = '\n'.join(line.rstrip() for line in markdown.split('\n'))
# Asegurar una sola línea en blanco 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 temprano:
def validate_markdown(markdown):
"""Validar calidad del markdown"""
issues = []
# Verificar restos de HTML
if '<' in markdown and '>' in markdown:
issues.append("Detectados tags HTML")
# Verificar enlaces rotos
if '[' in markdown and ']()' in markdown:
issues.append("Detección de enlace vacío")
# Verificar bloques de código excesivos
code_block_count = markdown.count('```')
if code_block_count % 2 != 0:
issues.append("Bloque de código no cerrado")
return len(issues) == 0, issues
4. Plantilla de procesamiento por lotes
Cuando procesas 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
html2textpor su simplicidad y ausencia de dependencias - Lógica personalizada: Usa
markdownifypara máxima flexibilidad mediante subclase - Scraping web: Usa
trafilaturapara extracción inteligente de contenido con eliminación de elementos innecesarios - Migraciones masivas: Usa
html2mdpara rendimiento asincrónico en proyectos a gran escala - Sistemas de producción: Usa
html-to-markdownpara seguridad de tipos y soporte completo de HTML5 - Preservación semántica: Usa
domscribepara 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:
- Comenzar con
trafilaturapara la extracción inicial de contenido — elimina navegación, anuncios y elementos innecesarios mientras preserva el contenido principal - Recurrir a
html-to-markdownpara 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
Todas estas herramientas (excepto html2text) están activamente manteniéndose y listas para producción. Es mejor:
- Instalar 2-3 bibliotecas que coincidan con tu caso de uso
- Probarlas con tus muestras reales de HTML
- Benchmarkear el rendimiento con los tamaños típicos de documentos
- 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 ha madurado significativamente, y no te equivocarás con ninguna de estas opciones para sus casos de uso específicos.
Recursos adicionales
- Documentación de html2text
- markdownify en PyPI
- html-to-markdown en GitHub
- Documentación de trafilatura
- Documentación de html2md
- domscribe en PyPI
Nota: Esta comparación se basa en el análisis de la documentación oficial, comentarios de la comunidad y la arquitectura de la biblioteca. Las características de rendimiento son representativas de los patrones típicos de uso. Para casos de uso específicos, ejecuta tus propios benchmarks con tus muestras reales de HTML.
Otros artículos útiles
- Hoja de trucos de Markdown
- Uso de bloques de código en Markdown
- Convertir documentos de Word a Markdown: Una guía completa
- Convertir contenido HTML a Markdown usando LLM y Ollama
- Hoja de trucos de cURL
- Hoja de trucos de Hugo, generador de sitios estáticos
- Añadir marcado de datos estructurados a Hugo
- Dokuwiki - wiki autohospedado y alternativas
- Usar Obsidian para gestión de conocimiento personal