Descripción
El siguiente programa toma una URL como entrada, descarga su contenido en HTML y lo transforma automáticamente al formato utilizado por PmWiki, generando un fichero listo para integrarse en el wiki sin necesidad de ediciones manuales. El proceso consiste en obtener la página web, limpiar y adaptar su estructura al marcado propio de PmWiki y guardar el resultado en un archivo local, lo que facilita incorporar información externa de forma rápida y ordenada dentro del sistema.
- import re
- import sys
- from pathlib import Path
- from urllib.parse import urlparse
- from requests_html import HTMLSession, HTML
- import asyncio
- # Variable global modificable o sustituible por parámetros
- URL_GLOBAL = "https://misutmeeple.com/2026/05/resena-light-speed-arena/"
- class ExtractorWeb:
- """Responsable de descargar el contenido HTML renderizado de una URL."""
- def __init__(self):
- self.sesion = HTMLSession()
- def descargar_html_renderizado(self, url: str) -> str:
- """Descarga la URL y fuerza el renderizado de JavaScript configurando el event loop."""
- try:
- try:
- bucle_eventos = asyncio.get_event_loop()
- except RuntimeError:
- bucle_eventos = asyncio.new_event_loop()
- asyncio.set_event_loop(bucle_eventos)
- respuesta = self.sesion.get(url)
- respuesta.raise_for_status()
- respuesta.html.render(timeout=20)
- return respuesta.html.html
- except Exception as error:
- raise RuntimeWarning(f"Error al descargar o renderizar la URL: {error}")
- class TransformadorPmWiki:
- """Responsable de transformar código HTML a la sintaxis oficial de PmWiki."""
- def transformar(self, contenido_html_completo: str) -> str:
- """
- Aísla el contenido de la etiqueta <body>, elimina scripts y lo transforma
- aplicando exhaustivamente las reglas de PmWiki.
- """
- # 1. Aislar el contenido del <body>
- objeto_html = HTML(html=contenido_html_completo)
- nodo_body = objeto_html.find("body", first=True)
- html_a_procesar = nodo_body.html if nodo_body else contenido_html_completo
- # --- MEJORA: Eliminar bloques <script> antes de procesar el texto ---
- # Se remueven tanto las etiquetas de apertura/cierre como todo el código JS de su interior
- html_a_procesar = re.sub(r"<script\b[^>]*>([\s\S]*?)</script>", "", html_a_procesar, flags=re.IGNORECASE)
- # 2. Procesar elementos complejos (Tablas)
- texto = self._procesar_tablas(html_a_procesar)
- # --- 3. Encabezados (Headings) ---
- texto = re.sub(r"<h1>(.*?)</h1>", r"! \1\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<h2>(.*?)</h2>", r"!! \1\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<h3>(.*?)</h3>", r"!!! \1\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<h4>(.*?)</h4>", r"!!!! \1\n", texto, flags=re.IGNORECASE)
- # --- 4. Estilos de Carácter / Énfasis ---
- texto = re.sub(r"<strong><em>(.*?)</em></strong>", r"'''''\1'''''", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<b><i>(.*?)</i></b>", r"'''''\1'''''", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<strong>(.*?)</strong>", r"'''\1'''", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<b>(.*?)</b>", r"'''\1'''", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<em>(.*?)</em>", r"''\1''", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<i>(.*?)</i>", r"''\1''", texto, flags=re.IGNORECASE)
- # Monospaciado, tachado, subrayado
- texto = re.sub(r"<code>(.*?)</code>", r"@@\1@@", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<tt>(.*?)</tt>", r"@@\1@@", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<del>(.*?)</del>", r"{-\1-}", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<s>(.*?)</s>", r"{-\1-}", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<ins>(.*?)</ins>", r"{+\1+}", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<u>(.*?)</u>", r"{+\1+}", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<sup>(.*?)</sup>", r"^\1^", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<sub>(.*?)</sub>", r"_\1_", texto, flags=re.IGNORECASE)
- # Enlaces
- texto = re.sub(r'<a\s+(?:[^>]*?\s+)?href="([^"]*)"[^>]*>(.*?)</a>', r"[[\1 | \2]]", texto, flags=re.IGNORECASE)
- # Estructuras (Líneas y Listas)
- texto = re.sub(r"<hr\s*/?>", r"\n----\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<ul>\s*<li>", r"\n* ", texto, flags=re.IGNORECASE)
- texto = re.sub(r"</ul>", r"\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"</li>\s*<li>\s*<ul>\s*<li>", r"\n** ", texto, flags=re.IGNORECASE)
- texto = re.sub(r"</li>\s*<li>", r"\n* ", texto, flags=re.IGNORECASE)
- texto = re.sub(r"</li>", r"", texto, flags=re.IGNORECASE)
- # Listas ordenadas
- texto = re.sub(r"<ol>\s*<li>", r"\n# ", texto, flags=re.IGNORECASE)
- texto = re.sub(r"</ol>", r"\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"</li>\s*<li>", r"\n# ", texto, flags=re.IGNORECASE)
- # Párrafos y Saltos
- texto = re.sub(r"<br\s*/?>", r"\n", texto, flags=re.IGNORECASE)
- texto = re.sub(r"<p>(.*?)</p>", r"\n\1\n", texto, flags=re.IGNORECASE)
- # --- 5. Limpieza Final ---
- texto = re.sub(r"<[^>]+>", "", texto)
- texto = re.sub(r"\n{3,}", "\n\n", texto)
- return texto.strip()
- def _procesar_tablas(self, html_puro: str) -> str:
- objeto_html = HTML(html=html_puro)
- tablas_html = objeto_html.find("table")
- for tabla in tablas_html:
- bloque_pmwiki = []
- atributos_tabla = []
- if "border" in tabla.attrs:
- atributos_tabla.append(f"border={tabla.attrs['border']}")
- if "bgcolor" in tabla.attrs:
- atributos_tabla.append(f"bgcolor={tabla.attrs['bgcolor']}")
- config_tabla = f"||{' '.join(atributos_tabla)}" if atributos_tabla else ""
- if config_tabla:
- bloque_pmwiki.append(config_tabla)
- filas = tabla.find("tr")
- for fila in filas:
- linea_fila = ""
- celdas = fila.find("th, td")
- for celda in celdas:
- es_encabezado = celda.tag == "th"
- separador = "||!" if es_encabezado else "||"
- contenido_celda = celda.text.strip() if celda.text else ""
- estilo = celda.attrs.get("style", "").lower()
- align = celda.attrs.get("align", "").lower()
- if "text-align: right" in estilo or align == "right":
- linea_fila += f"{separador} {contenido_celda}"
- elif "text-align: center" in estilo or align == "center":
- linea_fila += f"{separador} {contenido_celda} "
- else:
- linea_fila += f"{separador}{contenido_celda} "
- if linea_fila:
- linea_fila += "||"
- bloque_pmwiki.append(linea_fila)
- tabla_html_original = tabla.html
- tabla_pmwiki_string = "\n" + "\n".join(bloque_pmwiki) + "\n"
- html_puro = html_puro.replace(tabla_html_original, tabla_pmwiki_string)
- return html_puro
- class GeneradorNombreArchivo:
- """Responsable de la lógica de negocio para generar nombres y rutas válidas."""
- @staticmethod
- def obtener_ruta_en_directorio_del_script(url: str) -> Path:
- """Genera la ruta absoluta final apuntando siempre al mismo directorio del script."""
- url_analizada = urlparse(url)
- ruta_completa = f"{url_analizada.netloc}{url_analizada.path}"
- limpio = re.sub(r"[^a-zA-Z0-9\.\-]", "", ruta_completa)
- nombre_fichero = f"{limpio}.pmwiki" if limpio else "resultado.pmwiki"
- directorio_del_script = Path(__file__).resolve().parent
- return directorio_del_script / nombre_fichero
- class AplicacionWebAPmWiki:
- """Orquestador de la aplicación (Fachada / Controlador)."""
- def __init__(self):
- self.extractor = ExtractorWeb()
- self.transformador = TransformadorPmWiki()
- def ejecutar(self, url: str) -> None:
- """Ejecuta el flujo completo de descarga, transformación y guardado."""
- print(f"Iniciando proceso para: {url}")
- contenido_html_completo = self.extractor.descargar_html_renderizado(url)
- contenido_pmwiki = self.transformador.transformar(contenido_html_completo)
- ruta_guardado = GeneradorNombreArchivo.obtener_ruta_en_directorio_del_script(url)
- ruta_guardado.write_text(contenido_pmwiki, encoding="utf-8")
- print(f"Archivo guardado exitosamente en: {ruta_guardado}")
- if __name__ == "__main__":
- aplicacion = AplicacionWebAPmWiki()
- aplicacion.ejecutar(URL_GLOBAL)
Python : PyDev : py Programación
Última modificación de la página el 24 May 2026 a las 18h30
Powered by
PmWiki