                   ┌───────────────────────────────────────────────────┐
                   │                      main()                       │
                   └──────────────┬────────────────────────────────────┘
                                  │
                     Lista de URLs de medios y leaks
                                  │
                                  ▼
        ┌───────────────────────────────────────────────────────────────┐
        │ register_processed_notifications(base_folder, urls)          │
        │  - Crea/lee processed_articles.txt                           │
        │  - Filtra duplicados                                         │
        └──────────────┬───────────────────────────────────────────────┘
                       │ urls_to_process
     ┌─────────────────┴───────────────────────────┐
     │                                             │
     ▼                                             ▼
┌──────────────────────────────┐          ┌──────────────────────────────┐
│ explore_and_extract_articles │          │   explore_wayback_machine    │
│ (crawling + extracción)      │          │ (consulta Wayback y extrae)  │
└────────────┬─────────────────┘          └────────────┬─────────────────┘
             │                                          │
             │ guarda                                   │ guarda
             ▼                                          ▼
   ┌────────────────────┐                      ┌────────────────────┐
   │  articulos/ (txt)  │                      │  articulos/ (txt)  │
   └────────────────────┘                      └────────────────────┘
             ▲                                          ▲
             │                                          │
             ▼                                          ▼
   ┌────────────────────┐                      ┌────────────────────┐
   │  archivos/ (bin)   │  ← descarga         │  (no aplica)       │
   └────────────────────┘                      └────────────────────┘

            ┌────────────────────────────────────────────────┐
            │ process_files(archivos/ → tokenized/)          │
            │ tokenize_all_articles(articulos/ → tokenized/) │
            └──────────────┬─────────────────────────────────┘
                           │
                           ▼
                  ┌────────────────────┐
                  │  tokenized/ (ids)  │  ← ids BERT (máx 512)
                  └────────────────────┘

                           ▼
                ┌──────────────────────────┐
                │  get_folder_info + logs  │
                └──────────────────────────┘

Entrada: url raíz, folders, processed_urls, size_limit, depth=0..6
                                │
                                ▼
                    ┌──────────────────────────┐
                    │ HTMLSession().get(url)   │
                    │ response.html.render()   │  ← render JS (headless)
                    └─────────────┬────────────┘
                                  │
                                  ▼
                       Conjunto de absolute_links
                                  │
                 ┌────────────────┴─────────────────┐
                 │                                  │
     Si extensión conocida                 Si página HTML genérica
    (.pdf .csv .txt .xlsx .docx            (sin extensión/otra cosa)
     .html .md .zip)                        │
                 │                          ▼
                 ▼                ┌──────────────────────────────┐
 ┌──────────────────────────┐     │  extract_and_save_article()  │
 │ download_and_save_file() │     │  - parse <title>, <p>        │
 │  → archivos/             │     │  - translate → clean → txt   │
 └───────────┬──────────────┘     │  - guardar en articulos/     │
             │                    └──────────────┬───────────────┘
             │ (tras cada acción)               │
             ▼                                   ▼ (recursivo)
  get_folder_info(articulos/, archivos/)  explore_and_extract_articles(link, depth+1)
             │
             ▼
   ¿total_size >= 50GB? ──► Sí: detener │ No: continuar


archivos/  ──────────────────────────────────────────────────────┐
                                                                  ▼
                 Para cada archivo según extensión:
        ┌─────────────────────────────────────────────────────────────┐
        │ .pdf  → read_pdf()      → texto                             │
        │ .csv  → read_csv()      → texto                             │
        │ .txt  → open().read()   → texto                             │
        │ .docx → read_docx()     → texto                             │
        │ .xlsx → read_xlsx()     → texto                             │
        │ .zip  → read_zip()      → texto concatenado                 │
        │ .html/.md → read_html_md() → format_content()(*) → texto    │
        └─────────────────────────────────────────────────────────────┘
                                   │
                                   ▼
                     translate_text(deep_translator → 'es')
                                   │
                                   ▼
            clean_text( BeautifulSoup strip + minúsculas
                        + quita URLs + solo letras/espacios
                        + colapsa espacios + STOPWORDS_ES )
                                   │
                                   ▼
         tokenize_and_save(texto_limpio, filename, tokenized/)
                                   │
                                   ▼
                    tokenized/ contiene IDs BERT (máx 512)
(*) Nota: `format_content()` está vacío en tu snippet; hoy actúa como no-op.







articulos/ (txt limpios/es) ─────► para cada .txt:
                                     │
                                     ▼
           tokenizer.encode(text, max_length=512, add_special_tokens=True)
                                     │
                                     ▼
                    'id id id ...' → escribe en tokenized/ con
                       mismo nombre de archivo



clean_filename(name)
  - reemplaza \ / * ? : " < > | por "_"
  - espacios → '_'
  - corta a 100 chars

register_processed_notifications(base_folder, urls)
  - lee base_folder/processed_articles.txt (si existe)
  - devuelve solo URLs no vistas
  - añade nuevas al final (append)

explore_wayback_machine(url)
  - GET http://archive.org/wayback/available?url=...
  - si hay 'closest' → extract_and_save_article(archive_url)



translate_text(text) ──► GoogleTranslator(auto→'es') ──► texto_en_es
                                   │
                                   ▼
clean_text():
  1) quita CDATA variantes:  <! [ CDATA [ ... ] ] >
  2) BeautifulSoup → .get_text()
  3) lower()
  4) elimina URLs (regex http\S+)
  5) deja solo letras españolas y espacio (regex)
  6) colapsa espacios
  7) filtra STOPWORDS (lista extensa ES)


/var/www/theflows.net/flujos/FLUJOS_DATOS/NOTICIAS/
├── articulos/      (txt limpios en ES, de páginas HTML/Wayback)
├── archivos/       (descargas crudas: pdf, csv, xlsx, docx, zip, html, md, txt)
├── tokenized/      (mismos nombres, contenido = IDs BERT separados por espacio)
└── processed_articles.txt (histórico de URLs procesadas)


logging.basicConfig(level=INFO)
- Traza etapas (descarga, extracción, tokenización)
- Resumen final:
    * nº ficheros en articulos/, archivos/, tokenized/
    * tamaños totales (MB)
- Cortafuegos de tamaño: 50 GB entre articulos/ + archivos/



[ main() ]
   │
   ▼
register_processed_notifications()
   │  → filtra URLs ya procesadas
   ▼
+---------------------------+
| urls_to_process (nuevas) |
+---------------------------+
   │
   ▼
explore_and_extract_articles()
   │
   ├─► Si enlace a archivo (.pdf, .csv, .txt, .xlsx, .docx, .html, .md, .zip)
   │       └─► download_and_save_file() → guarda en /archivos
   │
   ├─► Si enlace HTML → extract_and_save_article()
   │       ├─ traduce (translate_text)
   │       ├─ limpia (clean_text)
   │       └─ guarda .txt en /articulos
   │
   └─► Recursivo hasta max_depth o límite 50 GB
         │
         ▼
explore_wayback_machine() → descarga versión archivada si existe
   │
   ▼
process_files(/archivos → /tokenized)
   │  ├─ read_pdf/csv/docx/xlsx/zip/html_md/txt
   │  ├─ translate_text()
   │  ├─ clean_text()
   │  └─ tokenize_and_save() con BERT
   │
   ▼
tokenize_all_articles(/articulos → /tokenized)
   │  └─ encode con BERT en IDs separados por espacio
   │
   ▼
get_folder_info() → logs resumen final


===========================================================
Flujo simplificado de procesamiento de un archivo
===========================================================

archivo descargado
   │
   ▼
read_*() según extensión
   │
   ▼
translate_text()  → GoogleTranslator(auto→es)
   │
   ▼
clean_text()
   ├─ elimina CDATA y HTML
   ├─ minúsculas, sin URLs, solo letras/es
   ├─ colapsa espacios
   └─ filtra stopwords ES
   │
   ▼
tokenize_and_save()
   ├─ tokenizer.encode(max 512 tokens)
   └─ guarda IDs BERT en /tokenized


===========================================================
Estructura de carpetas
===========================================================

NOTICIAS/
├── articulos/       ← .txt limpios en español
├── archivos/        ← binarios crudos descargados
├── tokenized/       ← tokens BERT (IDs)
└── processed_articles.txt ← historial de URLs


===========================================================
Control y límites
===========================================================

- Profundidad máxima de crawling: max_depth = 6
- Tamaño combinado artículos+archivos: límite 50 GB
- Evita duplicados con processed_articles.txt
- Logs detallados en consola



████████████████████████████████████  FLUJOS: ESQUEMA TÉCNICO (ASCII)  ████████████████████████████████████

[ ENTORNO / DEPENDENCIAS ]
- GoogleTranslator (deep_translator)         → API web de Google Translate (auto→es)
- requests / requests_html.HTMLSession       → HTTP + renderizado JS (chromium/headless)
- BeautifulSoup (bs4)                        → Parseo HTML, extracción de texto
- PyPDF2.PdfReader                           → Extracción texto de PDFs (si embebido/copiable)
- openpyxl                                   → Lectura de .xlsx
- python-docx (docx)                         → Lectura de .docx
- zipfile                                    → Descompresión y lectura (texto) de ficheros en ZIP
- html2text (no usado aquí en format_content)→ [placeholder]
- transformers.BertTokenizer                 → Tokenizador BERT ES (dccuchile/bert-base-spanish-wwm-cased)
- tqdm, logging, os, re, json, time, csv, hashlib, urllib.parse (urlparse/urljoin)  → utilidades

[ ESTRUCTURA DE CARPETAS (I/O) ]
/var/www/theflows.net/flujos/FLUJOS_DATOS/NOTICIAS/
├── articulos/        (TXT limpios en español, derivados de HTML/Wayback)
├── archivos/         (descargas crudas: .pdf .csv .txt .xlsx .docx .html .md .zip)
├── tokenized/        (TXT con IDs de tokens BERT, máx 512 tokens por archivo)
└── processed_articles.txt   (histórico de URLs ya procesadas → evita duplicados)

=============================================================================================================
[ main() ]
- Inicializa: URLs objetivo, rutas base, límite de 50GB, carpetas si no existen
- Flujo:
    1) urls_to_process = register_processed_notifications(base_folder, urls)
    2) Para url en urls_to_process:
         a) explore_and_extract_articles(url, articulos/, archivos/, processed_urls, size_limit)
         b) explore_wayback_machine(url, articulos/)
    3) process_files(archivos/ → tokenized/)
    4) tokenize_all_articles(articulos/ → tokenized/)
    5) get_folder_info() sobre cada carpeta y logging resumen
- Side effects: Escritura en articulos/, archivos/, tokenized/, processed_articles.txt; logs INFO

=============================================================================================================
[ register_processed_notifications(base_folder, urls) ]
- Lee/crea processed_articles.txt
- Devuelve: lista de URLs no presentes (nuevas)
- Efectos:
    * append de nuevas URLs al fichero
- Riesgos:
    * Fichero grande con el tiempo (puede optimizarse a DB/Set persistente)
    * No bloquea concurrencia (carreras si hay procesos paralelos)

=============================================================================================================
[ explore_and_extract_articles(url, articulos/, archivos/, processed_urls, size_limit, depth=0..6) ]
- Hace GET + render JS:
    session = HTMLSession(); response = session.get(url); response.html.render()
- Obtiene absolute_links (con JS resuelto)
- Para cada link:
    * Si ya está en processed_urls → skip
    * Marca link como procesado (set in-memory)
    * Detecta extensión: [.pdf .csv .txt .xlsx .docx .html .md .zip]
        - Coincide → download_and_save_file(link, archivos/)
        - mailto:/tel: → ignora
        - Otro/HTML → extract_and_save_article(link, articulos/); recursión depth+1
- Control de tamaño:
    * get_folder_info(articulos/) + get_folder_info(archivos/) → si ≥ 50GB → cortar
- Notas:
    * render() requiere Chromium instalado y recursos; costoso en CPU/RAM
    * Cuidado con sitios SPA/anti-bot; timeouts (30s)
    * max_depth=6 limita explosión de enlaces; se puede poner filtro de dominio

=============================================================================================================
[ download_and_save_file(url, archivos/) ]
- Descarga streaming (chunk 8192) con requests.get(url, stream=True, timeout=30)
- Filename = clean_filename(último segmento URL) || 'archivo_descargado'
- Escribe binario en archivos/
- Errores:
    * response.status_code != 200 → log
    * timeouts/conexión → log
- Seguridad:
    * No ejecuta nada; sólo guarda
    * Riesgo: HTML/JS guardado como .html/.md puede contener scripts (pero se procesa como texto después)

=============================================================================================================
[ extract_and_save_article(url, articulos/) ]
- GET simple (requests.get, timeout=30)
- Parse HTML: <title> y todos los <p> → concatena texto
- Procesa:
    * translate_text() (auto→es)
    * clean_text()
- Nombre archivo:
    * title → clean_filename(title) + '.txt'
    * fallback: último segmento de path URL + '.txt'
- Guarda .txt en articulos/
- Riesgos:
    * Páginas con contenido en divs/aria/role no capturado por <p> → menos texto
    * Limitaciones del traductor (cuotas, longitudes, errores temporales)
    * Si content vacío → log y skip

=============================================================================================================
[ explore_wayback_machine(url, articulos/) ]
- Consulta API: http://archive.org/wayback/available?url={url}
- Si hay 'closest' → archive_url → extract_and_save_article(archive_url)
- Usos:
    * Resiliencia ante 404/robots o contenido rotativo
- Riesgos:
    * No todas las páginas están archivadas
    * Rate limits

=============================================================================================================
[ process_files(archivos/, tokenized/) ]
- Itera archivos descargados por extensión:
    .pdf  → read_pdf()      (PdfReader.extract_text por página)
    .csv  → read_csv()      (csv.reader → " ".join(row))
    .txt  → open().read()   (texto tal cual)
    .docx → read_docx()     (docx.Document → concat párrafos)
    .xlsx → read_xlsx()     (openpyxl → concat celdas por fila)
    .zip  → read_zip()      (abre cada entrada, decode utf-8 ignore)
    .html/.md → read_html_md() → format_content()  [*format_content está vacía → no-op]
- Para cada contenido (si hay texto):
    translate_text() → clean_text() → tokenize_and_save(cleaned, filename, tokenized/)
- Notas:
    * Archivos binarios dentro del ZIP no-UTF8 se ignoran por decode errors (ignore)
    * PDF sin capa de texto → extract_text() puede devolver None
    * XLSX grande → memoria/tiempo; iter_rows() es razonable

=============================================================================================================
[ tokenize_all_articles(articulos/, tokenized/) ]
- Para cada .txt en articulos/:
    tokenizer.encode(text, truncation=True, max_length=512, add_special_tokens=True)
    → 'ids' separados por espacio → guarda con mismo filename en tokenized/
- Notas:
    * Truncation a 512 tokens: se pierde contenido largo (considerar sliding windows)
    * add_special_tokens=True añade [CLS]/[SEP]

=============================================================================================================
[ tokenize_and_save(text, filename, tokenized/) ]
- Encapsula la llamada a tokenizer.encode(...) con truncation=512
- Crea tokenized/ si no existe
- Escribe "id id id ..." en archivo de salida
- Riesgos:
    * Diferente encoding de entrada → normalizado por clean_text()
    * Si filename colisiona con otro (mismo nombre) → se sobrescribe

=============================================================================================================
[ translate_text(text) ]
- GoogleTranslator(source='auto', target='es').translate(text)
- Devuelve texto traducido o el original si error (catch + log)
- Limitaciones:
    * Longitudes excesivas → errores (“Text length need to be between 0 and 5000”)
      - Solución futura: fragmentar en bloques y recomponer
    * Rate limits/cambios API

=============================================================================================================
[ clean_text(text) ]
1) Quita CDATA: regex '<!\[\s*CDATA\s*\[.*?\]\]>' con flags=re.S
2) BeautifulSoup(text, 'html.parser').get_text(separator=" ")
3) lower()
4) Elimina URLs: regex r'http\S+'
5) Deja sólo letras españolas y espacios: r'[^a-záéíóúñü\s]' → ''
6) Colapsa espacios: r'\s+' → ' ' + strip()
7) Filtra STOPWORDS (set ES) palabra a palabra
- Resulta en texto normalizado listo para BERT
- Notas:
    * Pierde números, signos y acentos raros fuera de set
    * STOPWORDS puede ajustarse por dominio (noticias vs. técnico)

=============================================================================================================
[ read_pdf(pdf_path) ]
- Abre en binario, PdfReader(f)
- Recorre páginas → page.extract_text() → concat + '\n'
- Devuelve string (puede estar vacío)
- Limitaciones:
    * PDFs escaneados → sin OCR (no texto)
    * Layouts complejos → texto desordenado

[ read_csv(csv_path) ]
- csv.reader → por cada fila ' '.join(row) + '\n'
- Simple y robusto; no maneja tipos/formato especial

[ read_docx(docx_path) ]
- docx.Document → concat paragraph.text + '\n'
- Pierde estilos/tablas; conserva sólo texto base

[ read_xlsx(xlsx_path) ]
- openpyxl.load_workbook → por cada hoja → por cada fila
- ' '.join(str(cell.value or '')) + '\n'
- Pierde formato/tipos; sólo valores en orden de fila

[ read_zip(zip_path) ]
- zipfile.ZipFile → recorre cada entry
- z.open(filename).read().decode('utf-8', errors='ignore')
- Concatena todo a un sólo string
- Peligros: ZIP enorme → memoria; entries binarias → ignoradas por decode

[ read_html_md(file_path) ]
- open(file, 'utf-8', errors='replace').read()
- Retorna string crudo (sin limpieza HTML aquí)
- format_content() se invoca después (actualmente vacío)

[ format_content(html_content) ]
- [PLACEHOLDER] En el snippet está sin implementar.
- Potencial:
    * html2text → Markdown plano
    * Limpieza de scripts/estilos/menus
    * Normalización de espacios/entidades
- Hoy actúa como NO-OP (debería rellenarse)

=============================================================================================================
[ get_page_title(url) ]
- GET(url, timeout=10) → BeautifulSoup → <title>.text.strip()
- Devuelve None si falla o no hay <title>
- Usado para nombrar archivos de artículos

[ clean_filename(name) ]
- Reemplaza caracteres prohibidos [\/*?:"<>|] por "_"
- Espacios → "_"; corta a 100 chars
- Evita errores en FS; normaliza nombres

[ get_folder_info(path) ]
- Recorre recursivo → suma tamaño de ficheros y cuenta
- Devuelve (total_size_bytes, total_files)
- Usado para métricas y para detener por límite

=============================================================================================================
[ LOGGING / MÉTRICAS / LÍMITES ]
- logging.INFO por etapas (descargar, extraer, traducir, limpiar, tokenizar)
- Límite de tamaño: 50GB (archivos + artículos) → detiene crawling
- Resumen final:
    * Artículos descargados (# y MB)
    * Archivos descargados (# y MB)
    * Archivos tokenizados (# y MB)
- Sugerencias:
    * Añadir manejo de reintentos/backoff a requests
    * Cache de traducciones por hash (ahorro de coste/tiempo)
    * Paralelización controlada (cola + límites I/O/CPU)
    * Particionar tokenized/ por subcarpetas si #ficheros crece

=============================================================================================================
[ DATA FLOW (RESUMEN) ]
URLs  ──► register_processed_notifications ──► explore_* (HTMLSession/render/links)
   └────────► extract_and_save_article ──► translate_text ─► clean_text ─► articulos/*.txt
   └────────► download_and_save_file ─────────────────────────────────────► archivos/*
archivos/* ──► process_files (read_* → translate → clean → tokenize) ──► tokenized/*
articulos/*.txt ──► tokenize_all_articles ───────────────────────────────► tokenized/*
tokenized/*, articulos/*, archivos/* ──► get_folder_info + logs

████████████████████████████████████████████████████████████████████████████████████
