Cómo organizar automáticamente los archivos de tu PC con Python

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Automação em Python para organizar arquivos do computador

Una carpeta de Descargas con cientos de documentos, imágenes, instaladores y archivos comprimidos se vuelve difícil de mantener. Python puede clasificar esos elementos automáticamente según su extensión, fecha, tamaño o nombre. Con una estrategia segura, el script crea carpetas, mueve cada archivo a su destino y registra todas las acciones para que puedas revisar lo ocurrido.

Este proyecto combina varias habilidades importantes: trabajar con rutas mediante pathlib, manipular archivos con el módulo os, crear automatizaciones cotidianas y preparar scripts que se ejecutan al iniciar Windows. Antes de mover datos reales, prueba siempre con una carpeta temporal.

Planificar las categorías

El primer paso es definir qué extensiones pertenecen a cada carpeta. Un diccionario hace que la configuración sea legible:

CATEGORIAS = {
    "Imagenes": {".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"},
    "Documentos": {".pdf", ".doc", ".docx", ".txt", ".odt"},
    "Hojas_calculo": {".csv", ".xls", ".xlsx", ".ods"},
    "Comprimidos": {".zip", ".rar", ".7z", ".tar", ".gz"},
    "Audio": {".mp3", ".wav", ".flac", ".m4a"},
    "Video": {".mp4", ".mkv", ".avi", ".mov"},
    "Instaladores": {".exe", ".msi", ".dmg", ".deb"},
}

Las extensiones se guardan en minúsculas. Al analizar cada archivo también convertiremos su sufijo a minúsculas, de modo que FOTO.JPG y foto.jpg sean tratados de la misma manera.

Elegir la carpeta de origen

from pathlib import Path

ORIGEN = Path.home() / "Downloads"

if not ORIGEN.exists():
    raise FileNotFoundError(f"No existe la carpeta: {ORIGEN}")

En algunos sistemas en español la carpeta puede mostrarse como “Descargas”, pero su ruta interna seguir siendo Downloads. Comprueba el valor con print(ORIGEN). También puedes recibir la ruta desde un argumento de terminal o una variable de entorno.

Buscar la categoría de un archivo

def obtener_categoria(archivo: Path) -> str:
    extension = archivo.suffix.lower()

    for categoria, extensiones in CATEGORIAS.items():
        if extension in extensiones:
            return categoria

    return "Otros"

Path.suffix devuelve únicamente la última extensión. Para archivos como copia.tar.gz, quizá quieras analizar archivo.suffixes. En la mayoría de los casos, clasificar por .gz es suficiente.

Evitar sobrescribir archivos

Si la carpeta de destino ya contiene informe.pdf, mover otro archivo con el mismo nombre puede sobrescribirlo o provocar un error. Esta función crea un nombre disponible:

def nombre_disponible(destino: Path) -> Path:
    if not destino.exists():
        return destino

    contador = 1
    while True:
        candidato = destino.with_name(
            f"{destino.stem}_{contador}{destino.suffix}"
        )
        if not candidato.exists():
            return candidato
        contador += 1

Así se generan nombres como informe_1.pdf, informe_2.pdf y sucesivos. En proyectos empresariales, también puedes añadir una fecha o un identificador único.

Mover los archivos con seguridad

import shutil


def organizar_archivo(archivo: Path, simulacion: bool = True) -> Path:
    categoria = obtener_categoria(archivo)
    carpeta_destino = ORIGEN / categoria
    destino = nombre_disponible(carpeta_destino / archivo.name)

    if simulacion:
        print(f"[SIMULACION] {archivo.name} -> {destino}")
        return destino

    carpeta_destino.mkdir(parents=True, exist_ok=True)
    shutil.move(str(archivo), str(destino))
    return destino

El parámetro simulacion es una protección importante. Permite revisar el plan sin modificar nada. La documentación oficial de pathlib explica cómo representar rutas de manera portable, mientras que la documentación oficial de shutil describe las operaciones de copia y movimiento.

Recorrer únicamente los archivos adecuados

def organizar_carpeta(simulacion: bool = True) -> None:
    for elemento in ORIGEN.iterdir():
        if not elemento.is_file():
            continue

        if elemento.name.startswith("."):
            continue

        organizar_archivo(elemento, simulacion=simulacion)

No recorremos subcarpetas porque podríamos reorganizar estructuras que el usuario ya preparó. Tampoco movemos archivos ocultos. Puedes añadir exclusiones para el propio script, archivos temporales o descargas parciales como .crdownload y .part.

Ignorar archivos que todavía se están descargando

EXTENSIONES_TEMPORALES = {".crdownload", ".part", ".tmp"}

if elemento.suffix.lower() in EXTENSIONES_TEMPORALES:
    continue

Otra estrategia consiste en comprobar que el tamaño no cambie durante algunos segundos. Esto es útil cuando el navegador utiliza el nombre final antes de terminar la descarga:

import time


def esta_estable(archivo: Path, espera: int = 3) -> bool:
    inicial = archivo.stat().st_size
    time.sleep(espera)
    final = archivo.stat().st_size
    return inicial == final

No bloquees el programa demasiado tiempo si hay muchos archivos. Para vigilancia continua, una biblioteca como Watchdog puede reaccionar a eventos del sistema de archivos.

Registrar todas las acciones

import logging

logging.basicConfig(
    filename=ORIGEN / "organizador.log",
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(message)s",
    encoding="utf-8"
)

Después de mover:

logging.info("Movido: %s -> %s", archivo, destino)

Y si ocurre un error:

try:
    destino = organizar_archivo(elemento, simulacion=False)
except (PermissionError, OSError) as error:
    logging.exception("No se pudo mover %s: %s", elemento, error)

El registro permite investigar problemas y, si guardas origen y destino, construir una función para deshacer movimientos.

Crear un modo de deshacer

Una solución sencilla es escribir cada movimiento en CSV:

import csv
from datetime import datetime


def registrar_movimiento(origen: Path, destino: Path) -> None:
    historial = ORIGEN / "historial_movimientos.csv"
    nuevo = not historial.exists()

    with historial.open("a", newline="", encoding="utf-8") as archivo:
        escritor = csv.writer(archivo)
        if nuevo:
            escritor.writerow(["fecha", "origen", "destino"])
        escritor.writerow([
            datetime.now().isoformat(timespec="seconds"),
            str(origen),
            str(destino),
        ])

Para deshacer, lee las filas en orden inverso y mueve cada destino de vuelta a su origen, siempre que el archivo exista y el nombre original esté libre.

Organizar por año y mes

Las fotografías pueden ordenarse por fecha de modificación:

from datetime import datetime

fecha = datetime.fromtimestamp(archivo.stat().st_mtime)
carpeta = ORIGEN / "Imagenes" / str(fecha.year) / f"{fecha.month:02d}"

Ten en cuenta que la fecha de modificación no siempre representa la fecha de captura. Para fotografías, los metadatos EXIF pueden ser más precisos. Sin embargo, deben validarse porque pueden estar ausentes o dañados.

Script completo simplificado

from pathlib import Path
import logging
import shutil

ORIGEN = Path.home() / "Downloads"
CATEGORIAS = {
    "Imagenes": {".jpg", ".jpeg", ".png", ".webp"},
    "Documentos": {".pdf", ".docx", ".txt"},
    "Comprimidos": {".zip", ".rar", ".7z"},
}

logging.basicConfig(
    filename=ORIGEN / "organizador.log",
    level=logging.INFO,
    encoding="utf-8"
)


def categoria(archivo):
    extension = archivo.suffix.lower()
    for nombre, extensiones in CATEGORIAS.items():
        if extension in extensiones:
            return nombre
    return "Otros"


def destino_libre(destino):
    contador = 1
    candidato = destino
    while candidato.exists():
        candidato = destino.with_name(
            f"{destino.stem}_{contador}{destino.suffix}"
        )
        contador += 1
    return candidato


def main():
    for archivo in ORIGEN.iterdir():
        if not archivo.is_file() or archivo.name.startswith("."):
            continue
        if archivo.suffix.lower() in {".part", ".tmp", ".crdownload"}:
            continue

        carpeta = ORIGEN / categoria(archivo)
        carpeta.mkdir(exist_ok=True)
        destino = destino_libre(carpeta / archivo.name)

        try:
            shutil.move(str(archivo), str(destino))
            logging.info("%s -> %s", archivo, destino)
        except OSError:
            logging.exception("Error moviendo %s", archivo)


if __name__ == "__main__":
    main()

Programar la ejecución

En Windows puedes utilizar el Programador de tareas para ejecutarlo al iniciar sesión o cada hora. En Linux y macOS puedes utilizar cron o un servicio del sistema. No programes la tarea hasta haber probado el modo simulación, revisado las exclusiones y creado una copia de seguridad.

Buenas prácticas

No muevas archivos del sistema ni carpetas completas sin una regla explícita. Mantén una lista de exclusión, registra los cambios, evita sobrescrituras y prueba con copias. Si el script se ejecuta continuamente, impide que procese su propio archivo de log e historial. Añade límites para que un error de configuración no mueva miles de archivos fuera de la ubicación esperada.

Conclusión

Python permite convertir una carpeta caótica en una estructura ordenada con pocas funciones. pathlib representa las rutas, shutil.move realiza los movimientos y logging conserva un historial. La diferencia entre un experimento y una automatización confiable está en el modo simulación, la prevención de sobrescrituras, las exclusiones y la posibilidad de deshacer. Con esas protecciones, el organizador puede ejecutarse de forma periódica y ahorrar trabajo manual sin poner tus datos en riesgo.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Comparando listas e diferenças entre arrays em Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo comparar dos listas en Python y encontrar diferencias

    Compara listas en Python con igualdad, sorted, sets y Counter, conserva duplicados, detecta registros modificados y evita errores frecuentes.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Script Python configurado para iniciar junto com o Windows
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo ejecutar un script de Python al iniciar Windows

    Configura un script Python para iniciar con Windows usando la carpeta Inicio, archivos BAT, Task Scheduler, entornos virtuales, logs y

    Ler mais

    Tempo de leitura: 6 minutos
    11/07/2026
    Sistema de backup automático de arquivos usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo automatizar copias de seguridad con Python

    Aprende a automatizar copias de seguridad con Python usando pathlib, shutil, fechas, verificación, registros y una estrategia segura de restauración.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Execução de comandos do terminal usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Subprocess en Python: ejecutar comandos del terminal

    Aprende subprocess en Python para ejecutar comandos, capturar salida, manejar errores, establecer timeouts y evitar riesgos con shell=True.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    representação de automação com ícones de engrenagens, gráficos e pessoas no background
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Cómo automatizar tareas cotidianas con Python

    Aprende a automatizar tareas con Python: organizar archivos, renombrar lotes, procesar CSV, crear backups, usar logs y programar scripts.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    logo do BeautifulSoup em um fundo branco
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Web scraping con Python, Requests y BeautifulSoup

    Aprende web scraping con Python, Requests y BeautifulSoup: descarga páginas, selecciona elementos, limpia datos, pagina, maneja errores y exporta resultados.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026