Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Proyecto 1: Copias de Seguridad Automáticas

🧭 Navegación:

🔄 El problema: Proteger tus datos importantes

Todos hemos experimentado alguna vez la pérdida de archivos importantes: un documento en el que trabajamos durante horas, fotos familiares irremplazables, o información crítica para nuestro trabajo o estudios. Estas pérdidas pueden ocurrir por diversos motivos:

  • Fallas de hardware
  • Errores humanos (borrado accidental)
  • Malware o virus
  • Robo o daño físico del dispositivo
  • Corrupción de archivos

La solución es implementar un sistema de copias de seguridad (backups) que automáticamente resguarde tus archivos importantes de forma regular.

🎯 Objetivo del proyecto

Crearemos un script de Python que:

  1. Identifique archivos y carpetas importantes que necesitan respaldo
  2. Copie estos archivos a una ubicación segura (disco externo, carpeta en la nube, etc.)
  3. Organice los backups por fecha para facilitar la recuperación
  4. Comprima los archivos para ahorrar espacio
  5. Mantenga un registro (log) de las operaciones realizadas
  6. Se ejecute automáticamente según una programación definida

📋 Planificación del sistema

Componentes principales:

  1. Configuración: Definir qué archivos/carpetas respaldar y dónde
  2. Respaldo: Copiar los archivos a la ubicación de destino
  3. Organización: Estructurar los backups por fecha
  4. Compresión: Reducir el tamaño de los archivos respaldados
  5. Registro: Documentar las operaciones realizadas
  6. Programación: Ejecutar el script automáticamente

Bibliotecas que utilizaremos:

import os               # Para operaciones con el sistema de archivos
import shutil           # Para copiar archivos y directorios
import zipfile          # Para comprimir archivos
import datetime         # Para obtener la fecha actual
import logging          # Para registrar eventos
import schedule         # Para programar la ejecución automática
import time             # Para pausas en la ejecución

💻 Implementación paso a paso

Paso 1: Configuración inicial

Primero, definiremos las rutas de origen (archivos a respaldar) y destino (donde se guardarán los backups):

# backup_config.py

# Rutas de origen (archivos/carpetas a respaldar)
SOURCE_PATHS = [
    '/ruta/a/documentos_importantes',
    '/ruta/a/fotos_familiares',
    '/ruta/a/proyectos/python'
]

# Ruta de destino (donde se guardarán los backups)
BACKUP_DESTINATION = '/ruta/a/backups'

# Configuración de registro (logs)
LOG_FILE = '/ruta/a/backups/backup_log.txt'

# Programación (cuándo ejecutar el backup)
BACKUP_SCHEDULE = {
    'daily': '20:00',    # Todos los días a las 8:00 PM
    'weekly': 'monday',  # Cada lunes
}

Paso 2: Configuración del sistema de registro (logging)

Es importante mantener un registro de las operaciones realizadas:

# backup_logger.py

import logging
from datetime import datetime

def setup_logger(log_file):
    """Configura el sistema de registro."""
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        handlers=[
            logging.FileHandler(log_file),
            logging.StreamHandler()  # También muestra mensajes en consola
        ]
    )
    
    logging.info(f"=== Iniciando sesión de backup: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ===")
    
    return logging.getLogger()

Paso 3: Función principal de respaldo

Esta función se encargará de realizar la copia de seguridad:

# backup_functions.py

import os
import shutil
import zipfile
from datetime import datetime
import logging

def create_backup(source_paths, backup_destination):
    """
    Crea una copia de seguridad de los archivos especificados.
    
    Args:
        source_paths: Lista de rutas a respaldar
        backup_destination: Directorio donde se guardarán los backups
    
    Returns:
        str: Ruta del archivo de backup creado
    """
    # Crear carpeta de destino si no existe
    if not os.path.exists(backup_destination):
        os.makedirs(backup_destination)
        logging.info(f"Creado directorio de destino: {backup_destination}")
    
    # Crear subcarpeta con la fecha actual
    today = datetime.now().strftime('%Y-%m-%d')
    backup_dir = os.path.join(backup_destination, today)
    
    if not os.path.exists(backup_dir):
        os.makedirs(backup_dir)
        logging.info(f"Creado directorio para backup de hoy: {backup_dir}")
    
    # Nombre del archivo zip (con hora para evitar sobreescrituras)
    timestamp = datetime.now().strftime('%H-%M-%S')
    zip_filename = f"backup_{today}_{timestamp}.zip"
    zip_path = os.path.join(backup_dir, zip_filename)
    
    # Crear archivo zip
    with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
        # Procesar cada ruta de origen
        for source_path in source_paths:
            if os.path.exists(source_path):
                logging.info(f"Respaldando: {source_path}")
                
                # Si es un directorio, añadir todos sus contenidos
                if os.path.isdir(source_path):
                    for root, dirs, files in os.walk(source_path):
                        for file in files:
                            file_path = os.path.join(root, file)
                            # Guardar con ruta relativa dentro del zip
                            arcname = os.path.relpath(file_path, os.path.dirname(source_path))
                            zipf.write(file_path, arcname)
                            logging.debug(f"Añadido al zip: {file_path}")
                
                # Si es un archivo, añadirlo directamente
                elif os.path.isfile(source_path):
                    arcname = os.path.basename(source_path)
                    zipf.write(source_path, arcname)
                    logging.debug(f"Añadido al zip: {source_path}")
            else:
                logging.warning(f"Ruta no encontrada, omitiendo: {source_path}")
    
    logging.info(f"Backup completado: {zip_path}")
    logging.info(f"Tamaño del archivo: {os.path.getsize(zip_path) / (1024*1024):.2f} MB")
    
    return zip_path

Paso 4: Limpieza de backups antiguos

Para evitar que el espacio de almacenamiento se llene con backups antiguos:

def cleanup_old_backups(backup_destination, days_to_keep=30):
    """
    Elimina backups más antiguos que el número de días especificado.
    
    Args:
        backup_destination: Directorio donde se guardan los backups
        days_to_keep: Número de días a mantener los backups
    """
    if not os.path.exists(backup_destination):
        logging.warning(f"Directorio de backups no encontrado: {backup_destination}")
        return
    
    logging.info(f"Iniciando limpieza de backups antiguos (manteniendo {days_to_keep} días)")
    
    # Calcular la fecha límite
    cutoff_date = datetime.now() - datetime.timedelta(days=days_to_keep)
    
    # Revisar cada subdirectorio (que debería ser una fecha)
    for item in os.listdir(backup_destination):
        item_path = os.path.join(backup_destination, item)
        
        # Solo procesar directorios
        if os.path.isdir(item_path):
            try:
                # Intentar parsear el nombre del directorio como fecha
                dir_date = datetime.strptime(item, '%Y-%m-%d')
                
                # Si es más antiguo que la fecha límite, eliminar
                if dir_date < cutoff_date:
                    shutil.rmtree(item_path)
                    logging.info(f"Eliminado backup antiguo: {item_path}")
            except ValueError:
                # Si el nombre del directorio no es una fecha, ignorarlo
                logging.warning(f"Directorio con formato inesperado, ignorando: {item}")
    
    logging.info("Limpieza de backups antiguos completada")

Paso 5: Script principal

Ahora, uniremos todo en un script principal:

# backup_system.py

import schedule
import time
from backup_config import SOURCE_PATHS, BACKUP_DESTINATION, LOG_FILE, BACKUP_SCHEDULE
from backup_logger import setup_logger
from backup_functions import create_backup, cleanup_old_backups

def run_backup():
    """Ejecuta el proceso de backup completo."""
    logger = setup_logger(LOG_FILE)
    logger.info("Iniciando proceso de backup programado")
    
    try:
        # Crear backup
        backup_file = create_backup(SOURCE_PATHS, BACKUP_DESTINATION)
        
        # Limpiar backups antiguos (mantener últimos 30 días)
        cleanup_old_backups(BACKUP_DESTINATION, days_to_keep=30)
        
        logger.info("Proceso de backup completado exitosamente")
        return backup_file
    except Exception as e:
        logger.error(f"Error durante el proceso de backup: {str(e)}")
        return None

# Programar backups
if BACKUP_SCHEDULE.get('daily'):
    schedule.every().day.at(BACKUP_SCHEDULE['daily']).do(run_backup)
    print(f"Backup diario programado para las {BACKUP_SCHEDULE['daily']}")

if BACKUP_SCHEDULE.get('weekly'):
    if BACKUP_SCHEDULE['weekly'].lower() == 'monday':
        schedule.every().monday.at("00:00").do(run_backup)
    # Añadir más días según sea necesario
    print(f"Backup semanal programado para {BACKUP_SCHEDULE['weekly']}")

# Ejecutar un backup inmediato al iniciar
print("Ejecutando backup inicial...")
run_backup()

# Mantener el script en ejecución para que los backups programados funcionen
while True:
    schedule.run_pending()
    time.sleep(60)  # Verificar cada minuto

🚀 Uso del sistema

Configuración inicial

  1. Instala las dependencias necesarias:

    pip install schedule
    
  2. Modifica el archivo backup_config.py para especificar:

    • Las rutas que deseas respaldar
    • La ubicación donde se guardarán los backups
    • La programación de los backups
  3. Ejecuta el script principal:

    python backup_system.py
    

Ejecución como servicio

Para que el sistema funcione continuamente en segundo plano:

En Windows:

Puedes crear una tarea programada:

  1. Abre el Programador de tareas
  2. Crea una nueva tarea básica
  3. Configúrala para que se ejecute al inicio del sistema
  4. Apunta al script backup_system.py

En Linux:

Puedes crear un servicio systemd:

  1. Crea un archivo .service en /etc/systemd/system/
  2. Configúralo para ejecutar el script
  3. Habilita e inicia el servicio

🔍 Comprueba tu comprensión

  1. ¿Qué sucedería si una de las rutas de origen no existe?
  2. ¿Cómo modificarías el código para respaldar solo archivos modificados después de la última copia de seguridad?
  3. ¿Qué otras estrategias de respaldo podrías implementar además de la compresión en ZIP?
  4. ¿Cómo adaptarías el sistema para enviar notificaciones por correo electrónico cuando se complete un backup?

🛠️ Ideas para mejoras

  • Respaldo incremental: Respaldar solo los archivos que han cambiado desde el último backup
  • Cifrado: Añadir protección con contraseña a los archivos ZIP
  • Notificaciones: Enviar correos electrónicos o mensajes cuando se complete un backup
  • Interfaz gráfica: Crear una GUI simple para configurar y monitorear los backups
  • Respaldo en la nube: Integrar con servicios como Google Drive, Dropbox o AWS S3
  • Verificación: Comprobar la integridad de los archivos respaldados

📝 Resumen

En este proyecto, has creado un sistema completo de copias de seguridad automáticas que:

  • Respalda archivos y carpetas importantes
  • Organiza los backups por fecha
  • Comprime los archivos para ahorrar espacio
  • Mantiene un registro detallado de las operaciones
  • Se ejecuta automáticamente según una programación
  • Limpia backups antiguos para gestionar el espacio

Este sistema no solo te protege contra la pérdida de datos, sino que también te ha permitido aplicar conceptos importantes de Python como el manejo de archivos, la programación de tareas, el registro de eventos y la organización de código en módulos.

En el próximo proyecto, aprenderemos a organizar archivos automáticamente por tipo.