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

  1. Accede al panel > Add a URL to watch
  2. 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:

  1. Navigate to URL: URL inicial
  2. Click element: selector CSS del elemento a hacer clic
  3. Wait for text: esperar que aparezca un texto
  4. Select option: seleccionar en un dropdown
  5. 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.