Lección 28 de 29Módulo 6: Automatización y Mejores Prácticas

28. Scheduling con Cron

Programa scripts para ejecución automática periódica

20 minutos

Has creado reportes automáticos y sistemas de envío de emails, pero todavía requieren que ejecutes el script manualmente. En esta lección aprenderás a programar ejecuciones automáticas para que todo funcione sin intervención humana.

El problema de la ejecución manual

Incluso con scripts automatizados, si debes ejecutarlos manualmente:

  • Puedes olvidar ejecutarlos
  • No funcionan fuera de horario laboral o fines de semana
  • Requieren que estés disponible a horas específicas
  • No escalan (10 reportes = 10 ejecuciones manuales)

Solución: Scheduling automático que ejecuta scripts en horarios predefinidos.

Opciones de scheduling por sistema operativo

Linux/Mac: Cron

Cron es el scheduler nativo de Unix/Linux. Extremadamente confiable y usado en producción por millones de empresas.

Ventajas:

  • Integrado en el sistema operativo
  • Cero dependencias adicionales
  • Muy confiable
  • Logs nativos del sistema

Windows: Task Scheduler

Task Scheduler es el equivalente de Windows a cron.

Ventajas:

  • GUI intuitiva
  • Integración con Windows
  • Triggers avanzados

Python: Librería schedule

Librería Python multiplataforma para scheduling simple.

Ventajas:

  • Sintaxis Python simple
  • Multiplataforma
  • No requiere permisos de administrador

Cron: Fundamentos

Sintaxis de crontab

Cron usa 5 campos para definir cuándo ejecutar:

* * * * * comando_a_ejecutar
│ │ │ │ │
│ │ │ │ └─── Día de la semana (0-7, 0 y 7 = Domingo)
│ │ │ └───── Mes (1-12)
│ │ └─────── Día del mes (1-31)
│ └───────── Hora (0-23)
└─────────── Minuto (0-59)

Ejemplos comunes

# Cada día a las 8:00 AM
0 8 * * * /usr/bin/python3 /ruta/al/script.py

# Cada hora
0 * * * * /usr/bin/python3 /ruta/al/script.py

# Cada 15 minutos
*/15 * * * * /usr/bin/python3 /ruta/al/script.py

# Lunes a viernes a las 9 AM
0 9 * * 1-5 /usr/bin/python3 /ruta/al/script.py

# Primer día del mes a las 7 AM
0 7 1 * * /usr/bin/python3 /ruta/al/script_mensual.py

# Cada domingo a medianoche
0 0 * * 0 /usr/bin/python3 /ruta/al/script_semanal.py

Implementación práctica

Paso 1: Preparar script para producción

# daily_report.py
"""
Script de reporte diario - Optimizado para cron
"""

import sys
import os
from datetime import datetime
import logging

# Configurar logging para cron
logging.basicConfig(
    filename='/var/log/analytics/daily_report.log',
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def main():
    """Función principal"""
    try:
        logging.info("="*60)
        logging.info("INICIO - Reporte Diario")
        logging.info("="*60)

        # Importar después de configurar logging
        from report_generator import ReportGenerator
        from email_sender import EmailSender

        # Generar reportes
        logging.info("Generando reportes...")
        generator = ReportGenerator(data_path='/home/analytics/data/ventas.csv')

        pdf_file = generator.generar_reporte_pdf()
        excel_file = generator.generar_reporte_excel()
        html_file = generator.generar_reporte_html()

        metricas = generator.calcular_metricas()
        logging.info(f"Reportes generados: PDF, Excel, HTML")

        # Enviar emails
        logging.info("Enviando emails...")
        sender = EmailSender()

        sender.enviar_reporte_completo(
            grupo_destinatarios='todos',
            pdf_path=pdf_file,
            excel_path=excel_file,
            html_path=html_file,
            metricas=metricas
        )

        sender.cerrar_conexion()

        logging.info("="*60)
        logging.info("FIN - Proceso completado exitosamente")
        logging.info("="*60)

        return 0

    except Exception as e:
        logging.error(f"ERROR CRÍTICO: {e}", exc_info=True)
        return 1

if __name__ == "__main__":
    sys.exit(main())

Paso 2: Configurar cron en Linux/Mac

# 1. Abrir editor de crontab
# crontab -e

# 2. Agregar trabajos programados

# Reporte diario a las 8 AM
0 8 * * * /usr/bin/python3 /home/analytics/daily_report.py >> /var/log/analytics/cron.log 2>&1

# Reporte semanal cada lunes a las 9 AM
0 9 * * 1 /usr/bin/python3 /home/analytics/weekly_report.py >> /var/log/analytics/cron.log 2>&1

# Reporte mensual el primer día del mes a las 7 AM
0 7 1 * * /usr/bin/python3 /home/analytics/monthly_report.py >> /var/log/analytics/cron.log 2>&1

# Scraping de competidores cada 6 horas
0 */6 * * * /usr/bin/python3 /home/analytics/competitor_scraper.py >> /var/log/analytics/cron.log 2>&1

# Backup diario de datos a las 2 AM
0 2 * * * /usr/bin/python3 /home/analytics/backup_data.py >> /var/log/analytics/cron.log 2>&1

Paso 3: Verificar configuración

# Ver crontab actual
# crontab -l

# Ver logs de cron (Ubuntu/Debian)
# tail -f /var/log/syslog | grep CRON

# Ver logs de cron (CentOS/RHEL)
# tail -f /var/log/cron

# Verificar si cron está corriendo
# systemctl status cron

Windows: Task Scheduler

Crear tarea programada con GUI

  1. Abrir "Task Scheduler" (Programador de tareas)
  2. Click "Create Basic Task" (Crear tarea básica)
  3. Nombre: "Daily Analytics Report"
  4. Trigger: "Daily" a las 8:00 AM
  5. Action: "Start a program"
  6. Program: C:\Python39\python.exe
  7. Arguments: C:\analytics\daily_report.py
  8. Finish

Crear tarea con PowerShell

# PowerShell script para crear tarea
$Action = New-ScheduledTaskAction -Execute "C:\Python39\python.exe" -Argument "C:\analytics\daily_report.py"
$Trigger = New-ScheduledTaskTrigger -Daily -At 8am
Register-ScheduledTask -TaskName "Daily Analytics Report" -Action $Action -Trigger $Trigger -Description "Generates and sends daily analytics report"

Python: Librería schedule

Para desarrollo y testing, schedule es excelente:

# scheduler.py
"""
Scheduler Python multiplataforma
"""

import schedule
import time
from datetime import datetime
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

def reporte_diario():
    """Ejecuta reporte diario"""
    logging.info("Ejecutando reporte diario...")
    # Tu código aquí
    from daily_report import main
    main()

def reporte_semanal():
    """Ejecuta reporte semanal"""
    logging.info("Ejecutando reporte semanal...")
    # Tu código aquí

def scraping_competidores():
    """Scrapea precios de competidores"""
    logging.info("Scrapeando competidores...")
    # Tu código aquí

# Programar tareas
schedule.every().day.at("08:00").do(reporte_diario)
schedule.every().monday.at("09:00").do(reporte_semanal)
schedule.every(6).hours.do(scraping_competidores)

# Loop principal
print("Scheduler iniciado. Presiona Ctrl+C para detener.")
print("\nTareas programadas:")
for job in schedule.jobs:
    print(f"  - {job}")

try:
    while True:
        schedule.run_pending()
        time.sleep(60)  # Verificar cada minuto
except KeyboardInterrupt:
    print("\nScheduler detenido.")

Mejores prácticas

1. Usar rutas absolutas

# ❌ MAL - Rutas relativas
df = pd.read_csv('data/ventas.csv')

# ✅ BIEN - Rutas absolutas
df = pd.read_csv('/home/analytics/data/ventas.csv')

# ✅ MEJOR - Rutas configurables
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
df = pd.read_csv(os.path.join(BASE_DIR, 'data', 'ventas.csv'))

2. Redireccionar logs

# Capturar stdout y stderr
0 8 * * * /usr/bin/python3 /ruta/al/script.py >> /var/log/analytics/output.log 2>&1

# Explicación:
# >> /var/log/analytics/output.log  → Append stdout al archivo
# 2>&1                               → Redirigir stderr a stdout

3. Configurar variables de entorno

# En crontab, configurar PATH y variables necesarias
PATH=/usr/local/bin:/usr/bin:/bin
PYTHONPATH=/home/analytics/lib
EMAIL_PASSWORD=tu_app_password

0 8 * * * /usr/bin/python3 /home/analytics/daily_report.py

4. Notificaciones de errores

# script_wrapper.py
"""
Wrapper que envía email si el script falla
"""

import subprocess
import sys
from email_sender import EmailSender

def run_script_with_notifications(script_path):
    """Ejecuta script y notifica errores"""
    try:
        result = subprocess.run(
            ['/usr/bin/python3', script_path],
            capture_output=True,
            text=True,
            timeout=600  # 10 minutos máximo
        )

        if result.returncode != 0:
            # Script falló, enviar alerta
            sender = EmailSender()
            sender.enviar_email(
                destinatarios=['admin@tuempresa.com'],
                asunto=f"ERROR: Script {script_path} falló",
                cuerpo_html=f"""
                <h2>Error en script automatizado</h2>
                <p><strong>Script:</strong> {script_path}</p>
                <p><strong>Código de salida:</strong> {result.returncode}</p>
                <h3>Output:</h3>
                <pre>{result.stdout}</pre>
                <h3>Errores:</h3>
                <pre>{result.stderr}</pre>
                """
            )
            return 1

        return 0

    except subprocess.TimeoutExpired:
        # Script tomó demasiado tiempo
        sender = EmailSender()
        sender.enviar_email(
            destinatarios=['admin@tuempresa.com'],
            asunto=f"TIMEOUT: Script {script_path}",
            cuerpo_html=f"<p>El script excedió el tiempo máximo de ejecución.</p>"
        )
        return 1

if __name__ == "__main__":
    sys.exit(run_script_with_notifications(sys.argv[1]))

Monitoreo y debugging

Ver historial de ejecuciones

# En Linux, ver últimas ejecuciones de cron
grep CRON /var/log/syslog | tail -20

# Ver solo tu usuario
grep "$(whoami)" /var/log/syslog | grep CRON | tail -10

Testing antes de programar

# 1. Ejecutar script manualmente primero
python3 /home/analytics/daily_report.py

# 2. Verificar que genera output correcto
# 3. Revisar logs
cat /var/log/analytics/daily_report.log

# 4. Solo después programar en cron

Debugging de problemas comunes

Problema 1: Script no ejecuta

# Verificar permisos de ejecución
ls -l /home/analytics/daily_report.py

# Dar permisos si es necesario
chmod +x /home/analytics/daily_report.py

Problema 2: Python no encontrado

# Usar ruta absoluta de Python
which python3  # Copiar output

# Usar ese path en crontab
0 8 * * * /usr/bin/python3 /home/analytics/daily_report.py

Problema 3: Variables de entorno faltantes

# Cron tiene PATH limitado, especificar todo
PATH=/usr/local/bin:/usr/bin:/bin
PYTHONPATH=/home/analytics
HOME=/home/analytics

0 8 * * * cd /home/analytics && /usr/bin/python3 daily_report.py

Casos de uso reales

1. E-commerce: Reportes múltiples

# Reporte de ventas diario (8 AM)
0 8 * * * /usr/bin/python3 /home/analytics/sales_report.py

# Análisis de inventario (cada 4 horas)
0 */4 * * * /usr/bin/python3 /home/analytics/inventory_check.py

# Reporte de marketing semanal (lunes 9 AM)
0 9 * * 1 /usr/bin/python3 /home/analytics/marketing_weekly.py

# Dashboard mensual (día 1 a las 7 AM)
0 7 1 * * /usr/bin/python3 /home/analytics/monthly_dashboard.py

2. Startup SaaS: Métricas en tiempo real

# Métricas de usuario cada 15 minutos
*/15 * * * * /usr/bin/python3 /home/analytics/user_metrics.py

# Reporte diario de MRR (7 AM)
0 7 * * * /usr/bin/python3 /home/analytics/mrr_report.py

# Análisis de churn semanal (domingo medianoche)
0 0 * * 0 /usr/bin/python3 /home/analytics/churn_analysis.py

3. Agencia de marketing: Clientes múltiples

# Reportes de clientes (cada hora durante horario laboral)
0 9-18 * * * /usr/bin/python3 /home/analytics/client_reports.py

# Scraping de métricas de competencia (cada 12 horas)
0 */12 * * * /usr/bin/python3 /home/analytics/competitor_metrics.py

Resumen de la lección

✅ Configuraste cron en Linux/Mac para ejecución automática ✅ Aprendiste Task Scheduler en Windows para scheduling ✅ Implementaste librería schedule de Python multiplataforma ✅ Aplicaste mejores prácticas de rutas absolutas, logs y monitoreo ✅ Creaste notificaciones automáticas de errores


🎯 Próxima lección: 29. Mejores Prácticas y Siguientes Pasos - Testing, deployment y cómo avanzar

En la última lección consolidarás todo lo aprendido con testing, logging profesional, versionado con Git y los siguientes pasos en tu carrera como analista de datos. ¡Estás a una lección de completar el curso!

¿Completaste esta lección?

Marca esta lección como completada. Tu progreso se guardará en tu navegador.