Organiza archivos del PC con Python

Actualizado el: 21/08/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

    Imagem ilustrativa de conteúdo Python para YouTube
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    YouTube con Python: descarga vídeos propios con yt-dlp

    Descarga copias de tus propios vídeos o contenido autorizado con Python y yt-dlp, validación, historial, límites, FFmpeg y prácticas responsables.

    Ler mais

    Tempo de leitura: 6 minutos
    12/07/2026
    Automação de postagens no Twitter usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Tweepy: automatiza publicaciones en X con Python

    Automatiza publicaciones en X con Tweepy, credenciales seguras, validación, deduplicación, programación, logs y prácticas responsables contra el spam.

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Geração e edição de planilhas Excel usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Excel con Python: crea y edita con openpyxl

    Crea y edita Excel con Pandas y openpyxl: hojas, fórmulas, estilos, filtros, validación, gráficos, rutas seguras y manejo de archivos

    Ler mais

    Tempo de leitura: 4 minutos
    12/07/2026
    Gerando aplicativo APK Android com Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    APK Android con Python, Kivy y Buildozer

    Genera un APK Android con Python, Kivy y Buildozer: configuración, permisos, recursos, compilación, pruebas, almacenamiento, firma y publicación.

    Ler mais

    Tempo de leitura: 6 minutos
    12/07/2026
    Web scraper de notícias em Python com envio para Telegram
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    Scraper de noticias con Python y Telegram

    Crea un scraper de noticias con Requests y Beautiful Soup, elimina duplicados y envía titulares a Telegram de forma segura

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Compactação de arquivos ZIP usando Python
    Automatización y Scripts
    Foto de perfil de Leandro Hirt da Academify

    zipfile: descomprime archivos ZIP con seguridad

    Descomprime archivos ZIP con Python de forma segura: valida rutas, evita Zip Slip, limita tamaños, verifica integridad y procesa archivos

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026