Herramienta de mantenimiento del Proyecto WWW (índices de secciones)
Descripción: Herramienta en Python para generar automáticamente páginas índice bilingües del Proyecto WWW a partir de archivos de texto con secciones y artículos.
Introducción
Esta herramienta de mantenimiento en Python genera automáticamente las páginas índice bilingües del Proyecto WWW a partir de archivos de texto con las secciones y los artículos publicados.
Archivos de entrada del índice
El script utiliza dos archivos de texto plano como fuente de datos: secciones-es.txt para la versión en español y secciones-en.txt para la versión en inglés.
Cada archivo está organizado por secciones; cada sección comienza con su nombre en una línea independiente, seguida de pares de líneas que contienen, primero, el título del artículo y luego la URL correspondiente.
Entre una sección y la siguiente se deja al menos una línea en blanco, lo que permite al script identificar correctamente el inicio de cada categoría de contenidos.
Funcionamiento general del script
El script lee ambos archivos de texto, interpreta la estructura de secciones, títulos y URLs, y construye en memoria una representación ordenada del contenido del Proyecto WWW.
A partir de esa representación, genera dos archivos HTML (output-es.html y output-en.html) que incluyen las pestañas de secciones, las listas de artículos y los bloques JSON-LD necesarios para los buscadores.
Detalles de implementación
Lectura de archivos y estructura interna (read_sections)
La función read_sections recorre cada archivo línea por línea, detecta el nombre de cada sección y agrupa los títulos y URLs en un diccionario, donde cada clave es el nombre de la sección y cada valor es la lista de artículos asociados.
La función admite URLs en texto plano y también URLs escritas con formato Markdown, que son limpiadas para extraer el enlace real antes de incorporarlo a la estructura interna.
Generación de JSON-LD (generate_json_ld)
La función generate_json_ld construye, para cada sección, un bloque de datos estructurados con el tipo BreadcrumbList de schema.org, incluyendo los títulos y las URLs de los artículos.
Estos bloques se insertan en el HTML final dentro de etiquetas <script type="application/ld+json">, mejorando la comprensión del contenido por parte de los motores de búsqueda.
Construcción del HTML y pestañas (write_html_and_jsonld)
La función write_html_and_jsonld genera la estructura HTML completa, añade el comentario descriptivo para buscadores y crea los botones de cada sección, que funcionan como pestañas de navegación.
Para cada sección, genera un contenedor con su título y una lista ordenada de enlaces a los artículos; además, adapta los textos visibles según el idioma y añade la sección de artículos destacados del Proyecto WWW.
Uso práctico en el Proyecto WWW
Para actualizar los índices, basta con editar secciones-es.txt y secciones-en.txt, añadiendo o modificando secciones, títulos y URLs según sea necesario.
Una vez guardados los cambios, se ejecuta el script en Python; esto genera los archivos output-es.html y output-en.html, cuyo contenido se copia en las páginas correspondientes del Proyecto WWW en español e inglés.
Código fuente completo
A continuación se muestra el código fuente completo del script, listo para ser copiado, adaptado o reutilizado en futuras tareas de mantenimiento del Proyecto WWW.
import json
def read_sections(file_path):
"""
Lee un archivo donde las categorías están separadas por líneas en blanco.
Cada categoría comienza con su nombre en una línea, seguida de pares de líneas:
nombre del artículo y URL.
Retorna un diccionario {categoria: [(nombre, url), ...], ...}
"""
sections = {}
current_category = None
with open(file_path, 'r', encoding='utf-8') as f:
lines = [line.rstrip('\n') for line in f]
i = 0
n = len(lines)
while i < n:
line = lines[i].strip()
# Saltar líneas vacías
if not line:
i += 1
continue
# Detectar categoría: línea después de línea vacía o al inicio
if current_category is None or (i > 0 and lines[i-1].strip() == ''):
current_category = line
if current_category not in sections:
sections[current_category] = []
i += 1
continue
# Si llegamos aquí, estamos dentro de una categoría y esta línea es nombre de artículo
name = line
url = None
# Mirar la siguiente línea para ver si es una URL
if i + 1 < n:
url_candidate = lines[i + 1].strip()
# Aceptamos URLs con o sin formato Markdown
if url_candidate.startswith('http') or url_candidate.startswith('['):
# Ejemplo de Markdown: [https://...](https://...)
if url_candidate.startswith('[') and '](' in url_candidate:
url_inside = url_candidate.split('](')[1].rstrip(')')
url = url_inside
else:
url = url_candidate.strip('[]')
i += 2
else:
i += 1
else:
i += 1
if current_category is None:
raise ValueError("Artículo sin categoría definida")
if url:
sections[current_category].append((name, url))
return sections
def generate_json_ld(category, items):
"""Genera la estructura JSON-LD para una categoría dada."""
item_list = []
position = 1
for name, url in items:
if url:
item_list.append({
"@type": "ListItem",
"position": position,
"item": {
"@id": url,
"name": name
}
})
position += 1
return {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"name": category,
"itemListElement": item_list
}
def write_html_and_jsonld(sections, language='es'):
"""
Genera el HTML completo (comentario + JSON-LD + contenido)
a partir del diccionario de secciones.
language:
'es' → textos visibles en español (output-es.html)
'en' → textos visibles en inglés (output-en.html)
"""
html = ""
# Comentario con descripción corta para motores de búsqueda / Blogger
if language == 'es':
html += "<!-- Secciones del Proyecto WWW, con enlaces a los artículos publicados en español. -->\n\n"
else:
html += "<!-- Sections of Proyecto WWW, with links to articles published in English. -->\n\n"
# JSON-LD por cada categoría
for category, items in sections.items():
json_ld = generate_json_ld(category, items)
json_ld_str = json.dumps(json_ld, ensure_ascii=False, indent=4)
html += f'<script type="application/ld+json">\n{json_ld_str}\n</script>\n\n'
# Bloque inicial según idioma
if language == 'es':
html += (
'<div>\n'
'\t<p>Secciones establecidas para el contenido del Proyecto WWW.</p><br>\n'
'\t<a id="resumen" class="subrayadoSolidoAnaranjado01 letra_capital">Resumen del contenido</a>:<br>\n'
'\t<p class="FuenteNoventaPorCiento" style="padding: 3%;">\n'
'\t\tCada una de las partes o divisiones establecidas para el contenido del Proyecto WWW\n'
'\t</p>\n'
'</div>\n\n'
)
else:
html += (
'<div>\n'
'\t<p>Sections established for the content of Proyecto WWW.</p><br>\n'
'\t<a id="resumen" class="subrayadoSolidoAnaranjado01 letra_capital">Content summary</a>:<br>\n'
'\t<p class="FuenteNoventaPorCiento" style="padding: 3%;">\n'
'\t\tEach of the parts or divisions established for the content of Proyecto WWW\n'
'\t</p>\n'
'</div>\n\n'
)
html += '<div class="contenedor_full">\n'
# Botones (pestañas) por categoría
for i, category in enumerate(sections.keys()):
is_active = (i == 0)
aria_expanded = 'true' if is_active else 'false'
id_attr = ' id="abrirPorDefecto"' if is_active else ''
# Nombre visible según idioma (Arte → Arte Visual, Art → Visual Art)
if language == 'es' and category == 'Arte':
visible_name = 'Arte Visual'
elif language == 'en' and category == 'Art':
visible_name = 'Visual Art'
else:
visible_name = category
html += (
f'<button class="etiqueta_enlace" aria-controls="{category.lower()}" '
f'aria-expanded="{aria_expanded}" tabindex="0" '
f'onclick="seccionAbrirPagina(\'{category.lower()}\', this)"{id_attr}>{visible_name}</button>\n'
)
# Contenido de cada categoría
for i, (category, items) in enumerate(sections.items()):
is_active = (i == 0)
aria_hidden = 'false' if is_active else 'true'
html += (
f'<div id="{category.lower()}" class="etiqueta_contenido" aria-hidden="{aria_hidden}">\n'
)
# Misma regla para el título de la sección
if language == 'es' and category == 'Arte':
visible_name = 'Arte Visual'
elif language == 'en' and category == 'Art':
visible_name = 'Visual Art'
else:
visible_name = category
html += f'\t<h3 class="separador">{visible_name}</h3>\n\t<ol style="font-size: 1.05882em;">\n'
for name, url in items:
if url:
safe_name = name.replace("'", "'").replace('"', """)
html += f"\t\t<li><a href='{url}'>{safe_name}</a>.</li>\n"
html += '\t</ol>\n</div>\n'
html += '</div><!-- contenedor_full -->\n'
# Sección Destacados / Featured
if language == 'es':
html += (
'<div id="destacados">\n'
'\t<h3>Destacados</h3>\n'
'\t<ol>\n'
"\t\t<li>\n"
"\t\t\t<a href='https://www.proyectowww.com.ar/search/label/DESTACADOS'>\n"
"\t\t\t\tListado de los artículos mas notorios o relevantes, de todas las secciones\n"
"\t\t\t</a>.\n"
"\t\t</li>\n"
'\t</ol>\n'
'</div>\n'
)
else:
html += (
'<div id="destacados">\n'
'\t<h3>Featured</h3>\n'
'\t<ol>\n'
"\t\t<li>\n"
"\t\t\t<a href='https://www.proyectowww.com.ar/search/label/DESTACADOS'>\n"
"\t\t\t\tList of the most notable or relevant articles from all sections\n"
"\t\t\t</a>.\n"
"\t\t</li>\n"
'\t</ol>\n'
'</div>\n'
)
return html
def main():
# Versión en español: leer secciones-es.txt → output-es.html
sections_es = read_sections('secciones-es.txt')
html_es = write_html_and_jsonld(sections_es, language='es')
with open('output-es.html', 'w', encoding='utf-8') as f:
f.write(html_es)
# Versión en inglés: leer secciones-en.txt → output-en.html
sections_en = read_sections('secciones-en.txt')
html_en = write_html_and_jsonld(sections_en, language='en')
with open('output-en.html', 'w', encoding='utf-8') as f:
f.write(html_en)
if __name__ == "__main__":
main()