Este proyecto es una herramienta en TypeScript desarrollada para extraer la información pública de la sección Procedimientos Sancionatorios del Sistema Nacional de Información de Fiscalización Ambiental (SNIFA), administrado por la SMA de Chile.
El scraper recorre los expedientes, extrae su metadata, los guarda en una base de datos SQLite y descarga todos los PDFs asociados. Está diseñado de manera eficiente utilizando peticiones HTTP directas (sin levantar navegadores pesados como Puppeteer) y cuenta con un sistema dinámico para sortear bloqueos por límite de peticiones (errores HTTP 429).
- Node.js (versión 22.5 o superior). Se requiere esta versión debido al uso del módulo nativo de SQLite (
node:sqlite). - npm (gestor de paquetes de Node).
- Docker (opcional, si se prefiere ejecutar en contenedores).
Clona este repositorio e instala sus dependencias:
git clone https://github.com/Seba-LupeLab/prueba-scraper.git
cd Scraper
npm installEl script cuenta con comandos y flags para controlar el alcance de la extracción.
Para comprobar que el scraper funciona correctamente sin descargar miles de archivos, puedes limitar la ejecución a los primeros 5 expedientes:
npm run scrape -- --max-expedientes 5El scraper creará una carpeta llamada output/ donde guardará los PDFs descargados, los metadatos y la base de datos snifa.db.
Para iniciar la descarga de todos los expedientes del sitio:
npm run scrapeNota: Si cancelas el proceso presionando Ctrl + C, el programa terminará ordenadamente los archivos que tenga a medias, guardará el progreso en SQLite y se detendrá. Al volver a ejecutar el comando, retomará la descarga desde donde quedó.
Para tareas programadas o ejecuciones periódicas, puedes usar el flag --incremental:
npm run scrape -- --incrementalEn este modo, el scraper lee los expedientes más recientes en el listado y se detiene automáticamente en cuanto detecta una racha de expedientes que ya tiene guardados en la base de datos SQLite. Esto evita tener que descargar miles de registros que no han cambiado, completándose en pocos segundos.
Si por cortes de red o caídas temporales del servidor algunos PDFs no se descargaron, la aplicación los registrará en output/failed-downloads.json. Puedes volver a intentar la descarga exclusiva de estos archivos fallidos con:
npm run retryEl proyecto incluye un Dockerfile optimizado en múltiples etapas (multi-stage) que ejecuta el scraper bajo un usuario seguro (no-root) y persiste los datos en un volumen.
docker build -t snifa-scraper .Levanta el contenedor montando un volumen llamado snifa-data para guardar los resultados:
docker run --rm -v snifa-data:/data snifa-scraper --max-expedientes 5Ideal para dejar programado en un servidor (por ejemplo, en un cron):
docker run --rm -v snifa-data:/data snifa-scraper --incrementalPara convertir la base de datos SQLite almacenada en el volumen en archivos legibles (expedientes.csv y expedientes.json en tu carpeta local) sin realizar peticiones de red:
docker run --rm -v snifa-data:/data snifa-scraper --export- Eficiencia: En lugar de emular el navegador, el código interactúa directamente con los endpoints de SNIFA (usando
axiosy parseando el HTML resultante concheerio). - Resiliencia (Manejo de 429): SNIFA cuenta con un rate limit estricto. El cliente HTTP implementa una estrategia adaptativa inspirada en el algoritmo TCP AIMD: ante un código de estado 429, reduce a la mitad su concurrencia, incrementa el delay entre peticiones y respeta el valor
Retry-Afterque indica el servidor antes de retomar. - Validación robusta: Cada página es evaluada mediante validaciones de estructura con Zod. Si el formato del HTML cambia en el servidor, el scraper avisa del problema de forma inmediata.
- Magic Bytes: La aplicación comprueba el encabezado binario de las descargas. Si el servidor entrega un archivo comprimido (RAR o ZIP) mal etiquetado como PDF, se detecta en caliente y se guarda con su extensión real.
Puedes ejecutar la suite de pruebas unitarias y de integración para comprobar la lógica de parseo, el cliente HTTP y el limitador de tasa:
# Ejecutar tests (Vitest)
npm test
# Comprobar el formato y estilos (ESLint)
npm run lint
# Validar tipos de TypeScript
npm run typecheck