Scheduling con Schedule y Cron

Lectura
35 min~6 min lectura

Scheduling con Schedule y Cron

La automatización de procesos no sería completa sin la capacidad de ejecutar tareas en momentos específicos sin intervención manual. En esta lección exploraremos dos herramientas fundamentales para programar tareas: la biblioteca Schedule para Python y el sistema Cron de Unix/Linux.

¿Qué es el Scheduling?

El scheduling o programación de tareas es la técnica que permite ejecutar código de forma automática en intervalos determinados o en momentos específicos. Imagina que necesitas enviar reportes diarios por correo, limpiar archivos temporales cada semana, o monitorear un servidor cada 15 minutos. Sin scheduling, tendrías que ejecutar manualmente cada tarea, lo cual es insostenible y propenso a errores.

Python ofrece la biblioteca schedule, una solución elegante y sencilla para programaciones dentro del propio script. Para sistemas operativos Unix-like, cron proporciona scheduling a nivel del sistema operativo, permitiendo ejecutar cualquier comando o script en horarios definidos.

La Biblioteca Schedule de Python

La biblioteca schedule es perfecta para tareas dentro de aplicaciones Python. Su API es intuitiva y permite encadenar métodos de forma fluida.

Instalación

La instalación es trivial mediante pip:

pip install schedule

Ejemplo Básico

import schedule
import time

def tarea_ejemplo():
    print("Ejecutando tarea programada...")

# Programar una tarea cada día a las 9:00
schedule.every().day.at("09:00").do(tarea_ejemplo)

# Programar cada 15 minutos
schedule.every(15).minutes.do(tarea_ejemplo)

# Programar cada hora
schedule.every().hour.do(tarea_ejemplo)

# Bucle infinito que ejecuta las tareas pendientes
while True:
    schedule.run_pending()
    time.sleep(1)

Métodos de Programación Disponibles

La biblioteca ofrece múltiples opciones de frecuencia:

  • schedule.every(segundos).seconds.do(funcion) - Ejecuta cada N segundos
  • schedule.every().minute.do(funcion) - Ejecuta cada minuto
  • schedule.every().hour.do(funcion) - Ejecuta cada hora
  • schedule.every().day.at("HH:MM").do(funcion) - Ejecuta a hora específica
  • schedule.every().monday.do(funcion) - Ejecuta cada lunes
  • schedule.every().week.do(funcion) - Ejecuta semanalmente

Ejemplo Práctico: Generador de Reportes

import schedule
import time
from datetime import datetime

def generar_reporte_diario():
    """Genera un reporte de ventas del día"""
    fecha = datetime.now().strftime("%Y-%m-%d")
    print(f"Generando reporte del {fecha}...")
    # Aquí iría la lógica de generación del reporte
    
def enviar_notificacion():
    """Envía una notificación de estado"""
    print("Enviando notificación de estado...")
    # Lógica de envío de notificación

# Programar múltiples tareas
schedule.every().day.at("08:00").do(generar_reporte_diario)
schedule.every(30).minutes.do(enviar_notificacion)

print("Sistema de reportes iniciado...")
while True:
    schedule.run_pending()
    time.sleep(1)

Cron: Scheduling a Nivel de Sistema Operativo

Cron es el daemon de programación de tareas en sistemas Unix/Linux. A diferencia de schedule, cron funciona fuera de Python, permitiendo ejecutar scripts, comandos o programas en horarios específicos definidos en archivos crontab.

Sintaxis de Cron

Cada línea en un crontab sigue esta estructura:

* * * * * comando
│ │ │ │ │
│ │ │ │ └─── Día de la semana (0-7, donde 0 y 7 son domingo)
│ │ │ └────── Mes (1-12)
│ │ └──────── Día del mes (1-31)
│ └────────── Hora (0-23)
└──────────── Minuto (0-59)

Caracteres Especiales

  • asterisco (*) - Cualquier valor
  • guión (-) - Rango de valores (1-5)
  • coma (,) - Lista de valores (1,3,5)
  • barra (/) - Paso/intervalo (*/15)

Ejemplos Prácticos de Cron

# Ejecutar cada minuto
* * * * * /usr/bin/python3 /home/usuario/script.py

# Ejecutar cada 15 minutos
*/15 * * * * /usr/bin/python3 /home/usuario/script.py

# Ejecutar a las 6:00 AM cada día
0 6 * * * /usr/bin/python3 /home/usuario/backup.py

# Ejecutar cada lunes a las 8:00 AM
0 8 * * 1 /usr/bin/python3 /home/usuario/reporte.py

# Ejecutar el primer día de cada mes a medianoche
0 0 1 * * /usr/bin/python3 /home/usuario/cierre_mensual.py

# Ejecutar cada hora durante horario laboral (8AM-6PM)
0 8-18 * * 1-5 /usr/bin/python3 /home/usuario/tarea_laboral.py

Gestión de Crontabs

# Ver el crontab actual
crontab -l

# Editar el crontab
crontab -e

# Eliminar todo el crontab
crontab -r

# Ver logs de cron (en sistemas Ubuntu/Debian)
sudo grep CRON /var/log/syslog

Comparación: Schedule vs Cron

Schedule es ideal para tareas dentro de aplicaciones Python que necesitan ejecutarse mientras la aplicación está en ejecución. Cron es mejor para tareas del sistema operativo, scripts independientes, o cuando necesitas que las tareas persistan incluso si la aplicación Python se cierra.

  • Schedule: Código Python puro, fácil de integrar, se ejecuta mientras el script corre, ideal para bots, servicios largos
  • Cron: A nivel de SO, persiste entre ejecuciones, mejor para scripts standalone, tareas de mantenimiento del sistema

Ejemplo Avanzado: Combinando Schedule con Logging

import schedule
import time
import logging
from datetime import datetime

# Configurar logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('tareas_programadas.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

def tarea_con_manejo_errores():
    """Tarea con manejo robusto de errores"""
    try:
        logger.info("Iniciando tarea programada")
        # Simular trabajo que podría fallar
        resultado = proceso_complejo()
        logger.info(f"Tarea completada: {resultado}")
        return resultado
    except Exception as e:
        logger.error(f"Error en tarea programada: {e}")
        raise

def proceso_complejo():
    # Tu lógica de negocio aquí
    return "Éxito"

# Limpiarjobsprevious schedule.every().day.at("09:00").do(tarea_con_manejo_errores)
schedule.every().friday.at("17:00").do(tarea_con_manejo_errores)

logger.info("Sistema de scheduling iniciado")
while True:
    schedule.run_pending()
    time.sleep(60)

Errores Comunes

1. No incluir un bucle de ejecución

Uno de los errores más frecuentes es definir tareas programadas pero olvidar el bucle que las ejecuta:

# ❌ INCORRECTO - Las tareas nunca se ejecutan
schedule.every().day.at("09:00").do(mi_tarea)
# El script termina aquí y las tareas nunca se ejecutan

# ✅ CORRECTO
schedule.every().day.at("09:00").do(mi_tarea)
while True:
    schedule.run_pending()
    time.sleep(1)

2. Confundir la sintaxis de Cron

Es común olvidar que en cron los campos van en orden específico. Un error típico:

# ❌ INCORRECTO - Executará cada minuto de cada hora del día 9
9 * * * * comando  # Esto ejecuta a los minutos 9, no a las 9:00

# ✅ CORRECTO - Executa a las 9:00 AM cada día
0 9 * * * comando  # Minuto 0, hora 9

3. No manejar excepciones en tareas programadas

Si una tarea falla y no se maneja la excepción, puede detener todo el proceso de scheduling:

# ❌ INCORRECTO - Una excepción puede detener todo
def tarea_peligrosa():
    datos = carga_externa()  # Si falla, todo se detiene
    procesa(datos)

# ✅ CORRECTO - Manejo robusto de errores
def tarea_segura():
    try:
        datos = carga_externa()
        procesa(datos)
    except Exception as e:
        print(f"Error en tarea: {e}")
        # Loguear pero no detener otras tareas
    
schedule.every().hour.do(tarea_segura)

Checklist de Dominio

  • Comprendo la diferencia entre scheduling a nivel de aplicación vs. sistema operativo
  • Sé instalar y utilizar la biblioteca schedule en Python
  • Puedo programar tareas con diferentes frecuencias (minutos, horas, días, semanas)
  • Entiendo la sintaxis de 5 campos de cron y sus caracteres especiales
  • Sé crear, editar y eliminar entradas en crontab
  • Implemento manejo de errores en tareas programadas
  • Elijo correctamente entre Schedule y Cron según el caso de uso
  • Configuro logging para monitorear tareas programadas
  • Identifico y soluciono los errores comunes de scheduling
  • Aplico las mejores prácticas de scheduling en proyectos reales