Changedetection.io: Monitorización de Cambios en Sitios Web
Changedetection.io es una herramienta self-hosted para detectar cambios en páginas web y recibir notificaciones automáticas, con soporte para filtros CSS/XPath para monitorizar partes específicas del contenido, renderizado con navegador real para páginas dinámicas JavaScript y múltiples canales de alerta. Desde monitorizar la disponibilidad de productos hasta rastrear cambios en normativas o precios de competidores, Changedetection.io ofrece flexibilidad profesional sin servicios externos. Esta guía cubre la instalación y configuración avanzada en Linux.
Requisitos Previos
- Linux con Docker y Docker Compose instalados
- 512 MB RAM (1 GB recomendado si usas el navegador integrado)
- Nginx como proxy inverso (opcional pero recomendado)
Instalación con Docker
# Crear directorio de datos
mkdir -p /opt/changedetection/datastore
# Instalación básica sin navegador (solo HTML estático)
docker run -d \
--name changedetection \
--restart unless-stopped \
-p 127.0.0.1:5000:5000 \
-v /opt/changedetection/datastore:/datastore \
dgtlmoon/changedetection.io
Docker Compose con navegador Playwright
Para monitorizar páginas con JavaScript, React, Vue, etc.:
# /opt/changedetection/docker-compose.yml
version: '3'
services:
changedetection:
image: dgtlmoon/changedetection.io
container_name: changedetection
hostname: changedetection
volumes:
- ./datastore:/datastore
environment:
- BASE_URL=https://cambios.tudominio.com
- PLAYWRIGHT_DRIVER_URL=ws://playwright-chrome:3000
depends_on:
playwright-chrome:
condition: service_started
restart: unless-stopped
ports:
- "127.0.0.1:5000:5000"
playwright-chrome:
hostname: playwright-chrome
image: browserless/chrome:1-puppeteer-22.4.0
restart: unless-stopped
environment:
SCREEN_WIDTH: 1920
SCREEN_HEIGHT: 1024
SCREEN_DEPTH: 16
ENABLE_DEBUGGER: "false"
PREBOOT_CHROME: "true"
CONNECTION_TIMEOUT: 300000
MAX_CONCURRENT_SESSIONS: 10
CHROME_REFRESH_TIME: 600000
DEFAULT_BLOCK_ADS: "true"
DEFAULT_STEALTH: "true"
cd /opt/changedetection
docker compose up -d
# Ver logs
docker compose logs -f changedetection
# Acceder al panel en http://localhost:5000
Proxy inverso con Nginx
server {
listen 443 ssl http2;
server_name cambios.tudominio.com;
ssl_certificate /etc/letsencrypt/live/cambios.tudominio.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/cambios.tudominio.com/privkey.pem;
# Proteger el acceso con autenticación básica
auth_basic "Changedetection Admin";
auth_basic_user_file /etc/nginx/.changedetection-htpasswd;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Crear credenciales de acceso
htpasswd -c /etc/nginx/.changedetection-htpasswd admin
nginx -t && systemctl reload nginx
certbot --nginx -d cambios.tudominio.com
Configuración Básica de Watches
Un "watch" es una URL monitoreada con su configuración:
Desde la interfaz web
- Accede al panel > Add a URL to watch
- Ingresa la URL y configura:
- Check interval: frecuencia de verificación
- Notification: canales de alerta
Desde la API
API_KEY="tu-api-key" # Configurar en Settings > API
HC_URL="https://cambios.tudominio.com"
# Agregar una URL a monitorizar
curl -X POST "$HC_URL/api/v1/watch" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://ejemplo.com/precios",
"tag": "precios competencia",
"title": "Precios de Competidor Principal",
"time_between_check": {
"hours": 4
}
}'
# Listar todos los watches
curl "$HC_URL/api/v1/watch" \
-H "x-api-key: $API_KEY" | jq '.[] | {uuid, url, title, last_changed}'
# Forzar verificación inmediata
curl -X GET "$HC_URL/api/v1/watch/UUID/recheck" \
-H "x-api-key: $API_KEY"
# Ver el historial de cambios de un watch
curl "$HC_URL/api/v1/watch/UUID/history" \
-H "x-api-key: $API_KEY" | jq '.'
# Pausar un watch
curl -X PUT "$HC_URL/api/v1/watch/UUID" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"paused": true}'
Filtros CSS y XPath
Los filtros permiten monitorizar solo partes específicas de una página, reduciendo falsos positivos por cambios en menús, anuncios o fechas:
Filtros CSS
En el panel web, edita un watch y ve a Filters & Triggers:
/* Monitorizar solo el precio de un producto */
.precio-producto
#producto-precio span.valor
/* Monitorizar el estado de un servicio */
.status-indicator
div[data-status]
/* Monitorizar tabla de precios */
table.comparativa-precios tbody
/* Monitorizar varias secciones */
.precio, .stock-disponible, .fecha-entrega
Filtros XPath
# Elemento por atributo específico
//div[@class='precio']
//span[@itemprop='price']
# Texto de un elemento específico
//h1[@class='titulo-producto']/text()
# Tabla de datos
//table[@id='tabla-precios']//tr
# Elemento que contiene cierto texto
//div[contains(@class, 'stock') and contains(text(), 'disponible')]
Regex y Text en el contenido
# En Filters & Triggers > "Ignore text that matches these regular expressions":
# Ignorar fechas para evitar falsos positivos
\d{1,2}/\d{1,2}/\d{4}
\d{2}:\d{2}:\d{2}
# Ignorar contadores de visitas
\d+ visitas
\d+ usuarios en línea
Renderizado con Navegador
Para páginas con contenido dinámico (JavaScript, SPA, etc.):
Configurar en el watch
En el panel web > Editar watch > Request > Fetch Method:
- requests (defecto): HTTP directo, rápido pero sin JS
- playwright / puppeteer: navegador real con JS completo
Acciones JavaScript
Puedes ejecutar acciones antes de capturar la página:
// En el watch: Browser Steps > Add Step > Execute JavaScript
// Hacer clic en un botón para mostrar más contenido
document.querySelector('.ver-mas-precios').click();
// Esperar a que un elemento aparezca
await page.waitForSelector('.tabla-completa', { timeout: 5000 });
// Rellenar un formulario (ej: búsqueda de vuelos)
document.querySelector('#origen').value = 'MAD';
document.querySelector('#destino').value = 'BCN';
document.querySelector('#buscar').click();
Browser Steps (pasos de navegación)
En el panel web, edita un watch y ve a Browser Steps:
- Navigate to URL: URL inicial
- Click element: selector CSS del elemento a hacer clic
- Wait for text: esperar que aparezca un texto
- Select option: seleccionar en un dropdown
- Fill field: rellenar un campo de texto
Ejemplo de flujo para monitorizar precios con login:
Step 1: Navigate to https://tienda.ejemplo.com/login
Step 2: Fill field - selector: #email, value: [email protected]
Step 3: Fill field - selector: #password, value: mi-password
Step 4: Click element - selector: button[type=submit]
Step 5: Wait for text - text: "Bienvenido"
Step 6: Navigate to URL - https://tienda.ejemplo.com/mis-precios
Canales de Notificación
Changedetection.io soporta notificaciones via Apprise, que incluye docenas de servicios:
Configurar notificaciones globales
En el panel web > Settings > Notifications:
# URLs de notificación (formato Apprise)
# Email
mailto://usuario:[email protected]/[email protected]
# Telegram
tgram://BOT_TOKEN/CHAT_ID
# Slack
slack://workspace/token/channel
# Discord
discord://webhook-id/webhook-token
# ntfy (servidor self-hosted)
ntfy://ntfy.tudominio.com/cambios-web?auth=Bearer TU_TOKEN
# Gotify
gotify://notificaciones.tudominio.com/TU_APP_TOKEN
# Múltiples canales (uno por línea en el campo de notificaciones)
Notificación por watch individual
En el panel > Editar watch > Notifications:
- Puedes sobrescribir las notificaciones globales
- Agregar URLs de Apprise específicas para ese watch
- Personalizar el asunto y cuerpo del mensaje
# Template personalizado del mensaje (formato Jinja2):
# - {{ watch_url }}: URL monitoreada
# - {{ watch_title }}: título del watch
# - {{ preview_text }}: extracto del texto cambiado
# - {{ diff_url }}: URL para ver el diff completo
# Ejemplo de plantilla personalizada:
# Asunto: Cambio detectado en {{ watch_title }}
# Cuerpo:
# Se detectó un cambio en: {{ watch_url }}
#
# Extracto:
# {{ preview_text }}
#
# Ver diff completo: {{ diff_url }}
API REST
API_KEY="tu-api-key"
HC_URL="https://cambios.tudominio.com"
# Importar lista de URLs en masa
curl -X POST "$HC_URL/api/v1/import" \
-H "x-api-key: $API_KEY" \
-H "Content-Type: text/plain" \
--data-binary @urls.txt
# El archivo urls.txt tiene una URL por línea
# Exportar todos los watches (backup de configuración)
curl "$HC_URL/api/v1/export" \
-H "x-api-key: $API_KEY" \
-o backup-watches.json
# Ver el diff del último cambio detectado
curl "$HC_URL/api/v1/watch/UUID/snapshot/latest" \
-H "x-api-key: $API_KEY" | head -50
# Obtener estadísticas
curl "$HC_URL/api/v1/systeminfo" \
-H "x-api-key: $API_KEY" | jq '.'
Solución de Problemas
Falsos positivos frecuentes
# Configurar filtros CSS para aislar el contenido importante
# En el watch: Filters & Triggers
# Agregar texto a ignorar con regex:
# - Fechas: \d{1,2}\s+de\s+\w+\s+de\s+\d{4}
# - Horas: \d{2}:\d{2}
# - Contadores: \d+ visitas
# Aumentar el intervalo de verificación para comparar
# snapshots más espaciadas en el tiempo
El navegador no renderiza la página correctamente
# Verificar que el contenedor de Playwright está corriendo
docker compose ps playwright-chrome
# Ver los logs del navegador
docker compose logs playwright-chrome --tail=30
# Aumentar el timeout para páginas lentas
# En el watch: Request > Requests timeout: 120 segundos
# Verificar conectividad entre contenedores
docker exec changedetection curl -s http://playwright-chrome:3000/json/version | jq .
Las notificaciones no llegan
# Probar la URL de Apprise directamente
docker exec changedetection python3 -c "
import apprise
apobj = apprise.Apprise()
apobj.add('mailto://user:[email protected]/[email protected]')
apobj.notify(title='Prueba', body='Mensaje de prueba')
"
# Ver los logs de notificaciones
docker compose logs changedetection | grep -i "notify\|alert\|error"
Error de SSL al monitorizar páginas HTTPS
# Para páginas con certificados autofirmados o inválidos:
# En el watch: Request > Ignore SSL error: activar
# O configurar globalmente en Settings > Fetcher
Conclusión
Changedetection.io es una herramienta de monitorización web versátil que va más allá de la simple detección de disponibilidad: sus filtros CSS/XPath, integración con navegadores reales y sistema de notificaciones multi-canal permiten implementar casos de uso complejos como vigilancia de precios, alertas de cambios en regulaciones y monitorización de competidores. La posibilidad de self-hosting garantiza la privacidad de las URLs monitoreadas y elimina los límites de cuota de los servicios comerciales equivalentes.


