# Scraper de Imágenes + Analizador Qwen3-VL — Contexto técnico FLUJOS **Fecha:** 2026-04-21 **Directorio:** `FLUJOS_DATOS/IMAGENES/` **Entorno:** `FLUJOS_DATOS/myenv/` (Python 3.11, venv) --- ## Componentes del módulo ``` FLUJOS_DATOS/IMAGENES/ ├── wikipedia_image_scraper.py # Descarga imágenes de Wikipedia por tema ├── image_analyzer.py # Analiza imágenes con Qwen3-VL-8B ├── image_comparator.py # Compara imágenes (similaridad visual) ├── mongo_helper.py # Utilidades MongoDB para este módulo ├── pipeline_imagenes.py # Orquestador del módulo (flags CLI) ├── requirements_imagenes.txt # Dependencias del módulo ├── model_cache/ # Modelo Qwen descargado (~16 GB) — en .gitignore └── output/ └── wiki_images/ # Imágenes descargadas — en .gitignore ├── cambio_climático/ ├── geopolítica_conflictos/ ├── seguridad_internacional_espionaje/ └── ... ``` --- ## Parte 1: wikipedia_image_scraper.py ### Qué hace Descarga imágenes de Wikipedia por tema usando la **Wikimedia REST API**. Para cada tema de FLUJOS busca artículos relacionados, extrae las imágenes de cada artículo y las descarga filtrando iconos/logos pequeños. ### Temas de FLUJOS que se scrapean (`TEMAS_FLUJOS`) ```python TEMAS_FLUJOS = [ "cambio climático", "geopolítica conflictos", "seguridad internacional espionaje", "libertad de prensa periodismo", "corporaciones poder económico", "populismo extremismo", "desinformación redes sociales", "privacidad vigilancia masiva", "biodiversidad medioambiente", "inteligencia artificial tecnología", ] ``` ### Filtros de calidad (imágenes que se descartan) ```python MIN_WIDTH = 200 # pixels MIN_HEIGHT = 200 # pixels MIN_BYTES = 20_000 # 20 KB mínimo SKIP_PATTERNS = [ "flag_", "Flag_", "icon", "Icon", "logo", "Logo", "symbol", "Symbol", "coat_of_arms", "commons-logo", "wiki", "Wiki", "question_mark", "edit-", "nuvola", "Nuvola", "pictogram", "OOjs", "Ambox", "Portal-", "Disambig", ] VALID_EXTENSIONS = {".jpg", ".jpeg", ".png", ".webp"} ``` ### Flujo interno ``` buscar_articulos(tema, lang='es') └── GET https://es.wikipedia.org/w/api.php?action=query&list=search&srsearch={tema} └── para cada artículo: get_article_images(titulo) └── GET https://es.wikipedia.org/w/api.php?action=query&prop=images └── para cada imagen: get_image_info(filename) → Wikimedia API └── descarga si pasa filtros └── guarda en output/wiki_images/{tema_slug}/ └── upsert en MongoDB imagenes_wiki ``` ### Deduplicación Upsert por `archivo` (nombre del fichero) en MongoDB `imagenes_wiki`. Si el fichero ya existe en disco, se salta la descarga. ### Uso CLI ```bash # Scrape de todos los temas FLUJOS, max 20 imgs/tema, con MongoDB python wikipedia_image_scraper.py --flujos --max 20 --mongo # Scrape de un tema concreto python wikipedia_image_scraper.py --tema "cambio climático" --max 30 --mongo # Con idioma inglés python wikipedia_image_scraper.py --tema "climate change" --lang en --max 40 ``` ### Documento MongoDB generado (colección `imagenes_wiki`) ```json { "archivo": "cambio_climático_003.jpg", "image_path": "/var/www/theflows.net/flujos/FLUJOS_DATOS/IMAGENES/output/wiki_images/cambio_climático/cambio_climático_003.jpg", "image_url": "https://upload.wikimedia.org/wikipedia/commons/...", "tema": "cambio climático", "subtema": "derretimiento glaciar ártico", "descripcion_wiki": "Glaciar en retroceso en el Ártico, 2019", "articulo_titulo": "Cambio climático en el Ártico", "fecha": ISODate("2026-04-21") } ``` --- ## Parte 2: image_analyzer.py (Qwen3-VL-8B) ### Modelo usado **Qwen/Qwen2.5-VL-7B-Instruct** (HuggingFace) - Tamaño: ~16 GB en disco (bfloat16) - Inferencia: CPU (sin GPU disponible actualmente) - RAM necesaria: ~16–18 GB para el modelo + ~2 GB por batch de 4 imágenes - Cache local: `IMAGENES/model_cache/` > El servidor tiene 64 GB RAM, por lo que cabe sin problema. En GPU (A100/RTX 3090+) sería 10–50x más rápido. ### Carga del modelo ```python from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor import torch model = Qwen2_5_VLForConditionalGeneration.from_pretrained( "Qwen/Qwen2.5-VL-7B-Instruct", torch_dtype=torch.bfloat16, # mitad de RAM vs float32 device_map="cpu", cache_dir=str(IMAGENES_DIR / "model_cache"), ) processor = AutoProcessor.from_pretrained( "Qwen/Qwen2.5-VL-7B-Instruct", cache_dir=str(IMAGENES_DIR / "model_cache"), ) ``` ### Prompt de análisis ```python PROMPT = """Analiza esta imagen y proporciona: 1. Una descripción concisa del contenido principal (máx. 2 frases). 2. El tema principal (elige uno): cambio climático, conflicto armado, economía, política, tecnología, sociedad, otro. 3. Lista de 3-5 palabras clave relevantes. Formato de respuesta: DESCRIPCIÓN: [descripción] TEMA: [tema] PALABRAS CLAVE: [kw1, kw2, kw3]""" ``` ### Procesamiento por batch ```python def analyze_batch(image_paths: list[Path]) -> list[dict]: """Procesa hasta 4 imágenes por llamada al modelo.""" ``` `batch_size=4` por defecto. Cada batch ocupa ~2 GB RAM adicionales. ### Resume automático Antes de analizar, verifica qué imágenes ya están en MongoDB `imagenes`: ```python def get_already_analyzed() -> set[str]: """Devuelve set de nombres de archivo ya en la colección imagenes.""" return {doc['archivo'] for doc in db['imagenes'].find({}, {'archivo': 1})} ``` Solo procesa imágenes no presentes en la BD. ### Priorización de imágenes ```python def get_known_article_titles() -> set[str]: """Títulos de artículos presentes en wikipedia/noticias.""" # Prioriza imágenes cuyo tema coincide con artículos existentes ``` Las imágenes de temas con más documentos de texto en la BD se procesan primero, para maximizar la probabilidad de que los nodos imagen queden conectados por comparaciones. ### Documento MongoDB generado (colección `imagenes`) ```json { "archivo": "cambio_climático_003.jpg", "image_path": "/var/www/.../output/wiki_images/cambio_climático/cambio_climático_003.jpg", "tema": "cambio climático", "subtema": "calentamiento global", "texto": "Imagen satelital del retroceso del glaciar ártico entre 1990 y 2023. Se observa una reducción significativa de la capa de hielo.", "keywords": ["glaciar", "ártico", "deshielo", "calentamiento", "satélite"], "fecha": ISODate("2026-04-21") } ``` --- ## Parte 3: pipeline_imagenes.py (orquestador) ### Flags CLI ```bash python pipeline_imagenes.py --scrape # solo scraping de imágenes python pipeline_imagenes.py --analizar # solo análisis Qwen python pipeline_imagenes.py --mongo # escribe resultados en MongoDB python pipeline_imagenes.py --solo-json # escribe JSON local en vez de Mongo python pipeline_imagenes.py --analizar --carpeta /ruta/custom --mongo ``` ### Integración en el pipeline maestro El `pipeline_maestro.py` lo llama así: **Fase 1 (scraping):** ```bash python wikipedia_image_scraper.py --flujos --max 20 --mongo ``` **Fase 2 (análisis):** ```bash python pipeline_imagenes.py --analizar \ --carpeta FLUJOS_DATOS/IMAGENES/output/wiki_images \ --mongo ``` --- ## Dependencias Python (requirements_imagenes.txt) ``` transformers>=4.45 torch>=2.1 qwen-vl-utils Pillow requests pymongo scikit-learn python-dotenv ``` Instalación en el venv: ```bash /var/www/theflows.net/flujos/FLUJOS_DATOS/myenv/bin/python3 -m pip install -r requirements_imagenes.txt ``` --- ## Estado actual (2026-04-21) - Imágenes en disco: ~155 imágenes en `output/wiki_images/` (10 temas × ~15 imgs) - Imágenes en `imagenes_wiki` (MongoDB): ~150 docs - Imágenes analizadas con Qwen en `imagenes` (MongoDB): proceso en curso / pendiente de primera ejecución completa - El modelo Qwen se descarga automáticamente la primera vez que se llama (~16 GB, puede tardar 30–60 min) ## Pendiente / mejoras futuras - Conectar nodos imagen a la colección `comparaciones` (requiere embeddings de imagen o comparación texto-descripción) - Activar GPU cuando esté disponible (cambiar `device_map="cpu"` → `"cuda"`) - Aumentar `--max` a 50+ imágenes por tema una vez el análisis esté validado - Añadir soporte para imágenes de noticias (no solo Wikipedia)