Скрипт на Python для автосортировки папки «Загрузки»: готовый код и автозапуск

    Опубликовано: 02.10.2026 • Время чтения: 4 мин.

    Папка «Загрузки» (Downloads) на рабочем компьютере почти всегда превращается в цифровую свалку. Туда стекаются торренты, временные архивы, установочные дистрибутивы, скриншоты, сканы паспортов и рабочие PDF-отчеты. Когда внутри скапливается более пятисот файлов, поиск нужного документа превращается в пытку, а ручная сортировка отнимает драгоценное время.

    Ниже представлен полноценный скрипт на Python, не требующий сторонних библиотек (работает на чистых встроенных модулях os, shutil, pathlib, logging, time). Он умеет:

    1. Автоматически раскладывать файлы по категориям (Документы, Изображения, Видео, Архивы, Программы, Аудио, Торренты, Код и Скрипты).
    2. Игнорировать незавершенные скачивания браузеров (.crdownload, .part, .tmp, .download).
    3. Корректно разрешать конфликты имен: если файл report.pdf уже есть в целевой папке, скрипт автоматически переименует копию в report_1.pdf, а не затрет исходник.
    4. Вести локальный лог-файл всех перемещений (organizer.log).
    5. Работать в двух режимах: разовый прогон или фоновый непрерывный мониторинг раз в N секунд.

    1. Исходный код скрипта organizer.py

    Создайте файл organizer.py в любой удобной папке (например, C:\Scripts\organizer.py или ~/scripts/organizer.py) и скопируйте в него этот код:

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    
    import os
    import shutil
    import time
    import logging
    from pathlib import Path
    
    # ================= КОНФИГУРАЦИЯ =================
    
    # Путь к папке загрузок (по умолчанию берется домашний каталог текущего пользователя)
    DOWNLOADS_DIR = Path.home() / "Downloads"
    
    # Лог-файл для аудита перемещений
    LOG_FILE = DOWNLOADS_DIR / "organizer.log"
    
    # Словарь сопоставления папок и расширений (в нижнем регистре)
    EXTENSION_MAP = {
        "Изображения": {
            ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".ico", ".tiff", ".raw", ".heic"
        },
        "Документы": {
            ".pdf", ".docx", ".doc", ".txt", ".rtf", ".odt", ".xlsx", ".xls", ".csv", ".pptx", ".ppt"
        },
        "Архивы": {
            ".zip", ".rar", ".7z", ".tar", ".gz", ".bz2", ".xz", ".iso"
        },
        "Программы": {
            ".exe", ".msi", ".apk", ".deb", ".rpm", ".dmg", ".pkg", ".appimage"
        },
        "Видео": {
            ".mp4", ".mkv", ".avi", ".mov", ".wmv", ".flv", ".webm", ".m4v"
        },
        "Аудио": {
            ".mp3", ".wav", ".flac", ".aac", ".ogg", ".m4a", ".wma"
        },
        "Торренты": {
            ".torrent"
        },
        "Код и Скрипты": {
            ".py", ".go", ".js", ".ts", ".html", ".css", ".json", ".xml", ".yaml", ".yml", ".sh", ".bat"
        }
    }
    
    # Временные расширения, которые нельзя трогать (идет процесс загрузки браузером)
    IGNORED_EXTENSIONS = {".crdownload", ".part", ".tmp", ".download"}
    
    # Имена файлов/папок, которые скрипт не должен перемещать
    IGNORED_NAMES = {"organizer.log", "desktop.ini", ".DS_Store"}
    IGNORED_NAMES.update(EXTENSION_MAP.keys())
    IGNORED_NAMES.add("Другое")
    
    # ================= НАСТРОЙКА ЛОГИРОВАНИЯ =================
    
    logging.basicConfig(
        filename=str(LOG_FILE),
        level=logging.INFO,
        format="%(asctime)s - %(levelname)s - %(message)s",
        encoding="utf-8"
    )
    
    def get_destination_folder(file_path: Path) -> Path:
        ext = file_path.suffix.lower()
        for folder_name, extensions in EXTENSION_MAP.items():
            if ext in extensions:
                return DOWNLOADS_DIR / folder_name
        return DOWNLOADS_DIR / "Другое"
    
    def resolve_duplicate_name(destination_folder: Path, file_name: str) -> Path:
        stem = Path(file_name).stem
        suffix = Path(file_name).suffix
        target_path = destination_folder / file_name
        counter = 1
    
        while target_path.exists():
            new_name = f"{stem}_{counter}{suffix}"
            target_path = destination_folder / new_name
            counter += 1
    
        return target_path
    
    def process_file(file_path: Path):
        if not file_path.is_file():
            return
    
        file_name = file_path.name
    
        # Пропуск системных и скрытых файлов
        if file_name in IGNORED_NAMES or file_name.startswith("."):
            return
    
        ext = file_path.suffix.lower()
        if ext in IGNORED_EXTENSIONS:
            return
    
        # Защита от блокировки: если размер файла все еще меняется (идет запись на диск)
        try:
            initial_size = file_path.stat().st_size
            time.sleep(0.5)
            if file_path.stat().st_size != initial_size:
                return  # Файл еще докачивается, пропустим до следующего цикла
        except Exception:
            return
    
        dest_folder = get_destination_folder(file_path)
        dest_folder.mkdir(parents=True, exist_ok=True)
    
        target_path = resolve_duplicate_name(dest_folder, file_name)
    
        try:
            shutil.move(str(file_path), str(target_path))
            log_msg = f"Перемещен: '{file_name}' -> '{dest_folder.name}/{target_path.name}'"
            print(log_msg)
            logging.info(log_msg)
        except Exception as err:
            error_msg = f"Ошибка перемещения '{file_name}': {err}"
            print(error_msg)
            logging.error(error_msg)
    
    def run_organizer_once():
        print(f"[*] Сканирование папки: {DOWNLOADS_DIR}")
        for item in DOWNLOADS_DIR.iterdir():
            if item.is_file():
                process_file(item)
        print("[+] Сканирование завершено.")
    
    def run_organizer_daemon(interval_seconds: int = 30):
        print(f"[*] Демон запущен. Мониторинг {DOWNLOADS_DIR} каждые {interval_seconds} сек. (Ctrl+C для выхода)")
        try:
            while True:
                for item in DOWNLOADS_DIR.iterdir():
                    if item.is_file():
                        process_file(item)
                time.sleep(interval_seconds)
        except KeyboardInterrupt:
            print("\n[!] Работа демона остановлена пользователем.")
    
    if __name__ == "__main__":
        # Запуск в режиме непрерывного фонового демона каждые 30 секунд
        run_organizer_daemon(interval_seconds=30)
    

    2. Пошаговый запуск и проверка

    1. Откройте командную строку или PowerShell:
    cd C:\Scripts
    python organizer.py
    
    1. Скрипт мгновенно создаст папки в ваших Загрузках, разложит существующие файлы и будет ожидать появления новых каждые 30 секунд.
    2. Скачайте тестовую картинку из браузера — в течение полуминуты она переместится в каталог Downloads/Изображения.

    3. Настройка тихого автозапуска при старте Windows

    Чтобы терминал не мозолил глаза при старте компьютера:

    Через Планировщик заданий (Task Scheduler)

    1. Переименуйте organizer.py в organizer.pyw (расширение .pyw говорит Windows запускать скрипт без создания черного окна консоли).
    2. Нажмите Win + R, введите taskschd.msc и нажмите Enter.
    3. В правом меню выберите «Создать простую задачу»:
      • Имя: Downloads Cleaner
      • Триггер: «При входе в Windows»
      • Действие: «Запустить программу»
      • Программа или сценарий: выберите pythonw.exe (путь: C:\Users\ВашеИмя\AppData\Local\Programs\Python\Python312\pythonw.exe).
      • Аргументы: укажите путь к файлу в кавычках: "C:\Scripts\organizer.pyw".
    4. Нажмите «Готово». Теперь сортировка работает незаметно в фоновом режиме.

    4. Настройка на сервере Linux / macOS (systemd unit)

    Создайте юнит sudo nano /etc/systemd/system/downloads-cleaner.service:

    [Unit]
    Description=Auto Downloads Organizer
    After=network.target
    
    [Service]
    Type=simple
    User=youruser
    ExecStart=/usr/bin/python3 /home/youruser/scripts/organizer.py
    Restart=always
    RestartSec=10
    
    [Install]
    WantedBy=multi-user.target
    

    Активируйте службу:

    sudo systemctl daemon-reload
    sudo systemctl enable --now downloads-cleaner.service
    

    Частые вопросы

    ❓ Не удалит ли скрипт файлы с одинаковыми именами?

    Скрипт проверяет наличие дубликатов и добавляет числовой суффикс (1, 2, 3), предотвращая перезапись.

    ❓ Что происходит с временными файлами скачивания?

    Расширения .crdownload, .part и .tmp внесены в черный список и игнорируются до завершения скачивания.

    ❓ Как запустить скрипт в скрытом режиме без черного окна консоли?

    В Windows сохраните файл с расширением .pyw вместо .py — тогда он запустится через pythonw.exe без открытия терминала.

    👨‍💻

    Об авторе: Редакция Гайдотеки

    Инструкция подготовлена специалистами портала. Мы лично проверяем каждый способ оплаты и настройки программ перед публикацией. Если метод перестает работать — мы обновляем гайд.