Автоматизация рутинных задач с помощью Python: 7 готовых скриптов
Автоматизация рутинных задач с помощью Python: сортировка и переименование файлов, бэкапы, Excel, письма, проверка сайтов, парсинг и запуск по расписанию.
Содержание · 12 разделов
- Что понадобится
- Сортировка файлов по папкам
- Массовое переименование файлов
- Резервная копия папки в zip
- Отчёт из CSV в Excel (openpyxl)
- Отправка письма с вложением (smtplib)
- Веб-запросы: проверка сайтов и курс валют
- Парсинг страницы (BeautifulSoup)
- Запуск скриптов по расписанию
- Планировщик заданий Windows
- cron в Linux и macOS
- Библиотека schedule
- Частые ошибки
- FileNotFoundError, хотя файл на месте
- UnicodeDecodeError при чтении CSV
- ModuleNotFoundError: No module named ‘openpyxl’
- Скрипт работает вручную, но не по расписанию
- Частые вопросы
- Что выбрать для Excel: openpyxl или pandas?
- Как запустить скрипт без чёрного окна консоли в Windows?
- Можно ли сделать из скрипта .exe для коллег?
- Как автоматизировать работу с Word и PDF?
- Можно ли управлять мышью, клавиатурой и браузером?
- Итог
Автоматизация рутинных задач с помощью Python — это короткие скрипты, которые делают за вас однообразную работу: раскладывают файлы по папкам, переименовывают сотни фотографий, собирают отчёт в Excel, отправляют письма и проверяют сайты. Для большинства таких задач хватает стандартной библиотеки (pathlib, shutil, csv, smtplib) и пары пакетов: openpyxl для Excel и requests для веб-запросов.
Ниже — 7 готовых скриптов, которые можно взять и подстроить под себя, а в конце — как запускать их по расписанию. Все примеры проверены на Python 3.12, вывод в блоках — результат реального запуска.
Что понадобится
- Установленный Python 3.10 или новее и умение запускать скрипты из терминала.
- Отдельное виртуальное окружение для скриптов автоматизации — так библиотеки не смешаются с другими проектами.
- Сторонние пакеты (ставятся одной командой):
python -m pip install openpyxl requests beautifulsoup4 schedule # Windows
python3 -m pip install openpyxl requests beautifulsoup4 schedule # macOS, Linux
| Задача | Чем решается |
|---|---|
| Разложить файлы по папкам | pathlib, shutil.move |
| Массово переименовать файлы | pathlib, Path.rename |
| Резервная копия в zip | shutil.make_archive |
| Отчёт из CSV в Excel | csv, openpyxl |
| Отправить письмо с вложением | smtplib, email.message |
| Проверить сайты, получить курс | requests, xml.etree |
| Вытащить данные со страницы | requests, BeautifulSoup |
| Запуск по расписанию | Планировщик ОС, cron, schedule |
Важно: скрипты, которые перемещают, переименовывают или удаляют файлы, сначала запускайте на копии папки. В скриптах сортировки и переименования для этого есть режим
DRY_RUN: скрипт только печатает, что собирается сделать.
Сортировка файлов по папкам
Классика автоматизации — навести порядок в «Загрузках». Скрипт смотрит на расширение каждого файла и переносит его в папку категории. Если файл с таким именем уже есть, к новому добавляется номер — ничего не перезаписывается.
from pathlib import Path
import shutil
SOURCE = Path.home() / "Downloads" # какую папку разбираем
DRY_RUN = True # сначала только показываем, что будет сделано
CATEGORIES = {
"Изображения": {".jpg", ".jpeg", ".png", ".gif", ".webp"},
"Документы": {".pdf", ".docx", ".doc", ".txt", ".odt"},
"Таблицы": {".xlsx", ".xls", ".csv"},
"Архивы": {".zip", ".rar", ".7z"},
"Установщики": {".exe", ".msi", ".dmg", ".pkg"},
}
def category_for(path: Path) -> str:
ext = path.suffix.lower()
for folder, extensions in CATEGORIES.items():
if ext in extensions:
return folder
return "Прочее"
def unique_target(target: Path) -> Path:
"""Если файл с таким именем уже есть, добавляет (1), (2)..."""
counter = 1
candidate = target
while candidate.exists():
candidate = target.with_name(f"{target.stem} ({counter}){target.suffix}")
counter += 1
return candidate
def sort_folder(source: Path, dry_run: bool = True) -> None:
for path in sorted(source.iterdir()):
if not path.is_file():
continue # подпапки не трогаем
target_dir = source / category_for(path)
target = unique_target(target_dir / path.name)
print(f"{path.name} -> {target_dir.name}/")
if not dry_run:
target_dir.mkdir(exist_ok=True)
shutil.move(path, target)
if __name__ == "__main__":
sort_folder(SOURCE, dry_run=DRY_RUN)
archive.zip -> Архивы/
data.csv -> Таблицы/
notes.txt -> Документы/
photo.JPG -> Изображения/
report.pdf -> Документы/
setup.exe -> Установщики/
unknown.xyz -> Прочее/
Проверили вывод — поменяйте DRY_RUN на False и запустите ещё раз. Path.home() возвращает домашнюю папку пользователя на любой ОС, а path.suffix.lower() нужен, чтобы photo.JPG и photo.jpg попали в одну категорию.
Массовое переименование файлов
Фотографии с телефона называются IMG_2041.JPG, скриншоты — Screenshot 1.png. Скрипт переименовывает их по шаблону дата_префикс_номер, сортируя по времени изменения файла:
from datetime import datetime
from pathlib import Path
FOLDER = Path("photos")
PREFIX = "отпуск"
DRY_RUN = True
files = sorted(
(p for p in FOLDER.iterdir() if p.suffix.lower() in {".jpg", ".jpeg", ".png"}),
key=lambda p: p.stat().st_mtime, # по времени изменения файла
)
for number, path in enumerate(files, start=1):
date = datetime.fromtimestamp(path.stat().st_mtime).strftime("%Y-%m-%d")
new_name = f"{date}_{PREFIX}_{number:03d}{path.suffix.lower()}"
target = path.with_name(new_name)
if target.exists():
print(f"Пропуск: {new_name} уже существует")
continue
print(f"{path.name} -> {new_name}")
if not DRY_RUN:
path.rename(target)
IMG_2041.JPG -> 2026-07-14_отпуск_001.jpg
IMG_2042.JPG -> 2026-07-15_отпуск_002.jpg
Screenshot 1.png -> 2026-07-16_отпуск_003.png
Формат :03d дополняет номер нулями (001, 002), поэтому файлы правильно сортируются в проводнике. Проверка target.exists() обязательна: в Windows rename() на занятое имя вызовет FileExistsError, а в Linux и macOS молча перезапишет файл. Подробнее о форматировании дат — в статье о модуле datetime, о нумерации в цикле — в разборе функции enumerate.
Резервная копия папки в zip
Скрипт упаковывает папку в архив с датой и временем в имени и хранит только последние KEEP копий:
from datetime import datetime
from pathlib import Path
import shutil
SOURCE = Path("project") # что копируем
BACKUP_DIR = Path("backups") # куда складываем архивы
KEEP = 7 # сколько последних копий хранить
def make_backup() -> Path:
BACKUP_DIR.mkdir(exist_ok=True)
stamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
base_name = BACKUP_DIR / f"{SOURCE.name}_{stamp}"
archive = shutil.make_archive(str(base_name), "zip", root_dir=SOURCE)
return Path(archive)
def remove_old_backups() -> None:
archives = sorted(BACKUP_DIR.glob(f"{SOURCE.name}_*.zip"))
for old in archives[:-KEEP]:
old.unlink()
print("Удалён старый архив:", old.name)
if __name__ == "__main__":
archive = make_backup()
size_kb = archive.stat().st_size / 1024
print(f"Создан архив {archive.name} ({size_kb:.1f} КБ)")
remove_old_backups()
Создан архив project_2026-10-09_14-59-24.zip (0.3 КБ)
Удалён старый архив: project_2026-10-01_09-00-00.zip
Хитрость в формате даты ГГГГ-ММ-ДД_ЧЧ-ММ-СС: при такой записи обычная сортировка строк совпадает с хронологической, поэтому archives[:-KEEP] — это все архивы, кроме семи самых свежих. Двоеточия в имени не используем — Windows их запрещает.
Отчёт из CSV в Excel (openpyxl)
Типичная офисная задача: есть выгрузка продаж в CSV, нужен отчёт в Excel с итогами по менеджерам. Входной файл sales.csv:
date,manager,product,amount
2026-10-01,Анна,Ноутбук,85000
2026-10-01,Борис,Монитор,23000
2026-10-02,Анна,Мышь,1500
2026-10-03,Вера,Ноутбук,92000
2026-10-03,Борис,Клавиатура,4200
2026-10-04,Анна,Монитор,21000
import csv
from collections import defaultdict
from openpyxl import Workbook
from openpyxl.styles import Font
totals = defaultdict(int)
counts = defaultdict(int)
with open("sales.csv", encoding="utf-8", newline="") as f:
for row in csv.DictReader(f):
manager = row["manager"]
totals[manager] += int(row["amount"])
counts[manager] += 1
wb = Workbook()
ws = wb.active
ws.title = "Итоги"
ws.append(["Менеджер", "Сделок", "Сумма, ₽"])
for manager, total in sorted(totals.items(), key=lambda item: item[1], reverse=True):
ws.append([manager, counts[manager], total])
last = ws.max_row
ws.append(["Итого", f"=SUM(B2:B{last})", f"=SUM(C2:C{last})"])
for cell in ws[1] + ws[ws.max_row]:
cell.font = Font(bold=True)
for row in ws.iter_rows(min_row=2, min_col=3, max_col=3):
row[0].number_format = "#,##0"
ws.column_dimensions["A"].width = 15
ws.column_dimensions["C"].width = 14
ws.freeze_panes = "A2" # закрепить строку заголовка
wb.save("report.xlsx")
print(f"Готово: {len(totals)} менеджера, отчёт в report.xlsx")
В Excel получится таблица с жирными заголовком и итоговой строкой, разделителями разрядов и закреплённой первой строкой. Формулы =SUM(...) записываются как текст и вычисляются, когда файл открывает Excel или LibreOffice.
Читать Excel так же просто — load_workbook() и iter_rows(values_only=True):
from openpyxl import load_workbook
wb = load_workbook("report.xlsx")
ws = wb["Итоги"]
for name, deals, amount in ws.iter_rows(min_row=2, values_only=True):
print(f"{name:<8} {deals!s:<14} {amount}")
Анна 3 107500
Вера 1 92000
Борис 2 27200
Итого =SUM(B2:B4) =SUM(C2:C4)
Обратите внимание на последнюю строку: openpyxl не вычисляет формулы, а возвращает их текст. Параметр load_workbook(..., data_only=True) отдаёт значения, сохранённые Excel при последнем открытии файла; у файла, который ни разу не открывали в Excel, их нет. Если данных много и нужны группировки и сводные таблицы, удобнее pandas: pd.read_csv() и df.to_excel().
Отправка письма с вложением (smtplib)
Готовый отчёт можно сразу отправить по почте. В стандартной библиотеке для этого есть smtplib (подключение к почтовому серверу) и email.message.EmailMessage (сборка письма):
import os
import smtplib
from email.message import EmailMessage
from pathlib import Path
SMTP_HOST = "smtp.example.com" # SMTP-сервер вашей почты
SMTP_PORT = 465 # порт для SSL-подключения
SENDER = "robot@example.com"
RECIPIENT = "boss@example.com"
def build_message(attachment: Path) -> EmailMessage:
msg = EmailMessage()
msg["Subject"] = "Отчёт по продажам"
msg["From"] = SENDER
msg["To"] = RECIPIENT
msg.set_content("Добрый день!\n\nОтчёт во вложении.\n\n— Скрипт автоматизации")
msg.add_attachment(
attachment.read_bytes(),
maintype="application",
subtype="vnd.openxmlformats-officedocument.spreadsheetml.sheet",
filename=attachment.name,
)
return msg
def send(msg: EmailMessage) -> None:
password = os.environ["SMTP_PASSWORD"] # пароль не храним в коде
with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT) as server:
server.login(SENDER, password)
server.send_message(msg)
if __name__ == "__main__":
send(build_message(Path("report.xlsx")))
print("Письмо отправлено")
Адрес SMTP-сервера и порт смотрите в справке своего почтового сервиса. Большинство публичных сервисов не принимают основной пароль от аккаунта для сторонних программ — для скрипта нужно создать отдельный «пароль приложения» в настройках безопасности почты. Пароль передаётся через переменную окружения SMTP_PASSWORD, а не записывается в код: так он не попадёт в Git.
Веб-запросы: проверка сайтов и курс валют
Библиотека requests превращает HTTP-запрос в одну строку. Первый скрипт проверяет, отвечают ли сайты, и дописывает результат в журнал CSV:
import csv
from datetime import datetime
import requests
SITES = [
"https://www.python.org",
"https://docs.python.org/3/",
"https://example.com/no-such-page",
"https://no-such-domain.invalid",
]
def check(url: str) -> tuple[str, str]:
try:
response = requests.get(url, timeout=10)
except requests.RequestException as error:
return "ошибка", type(error).__name__
seconds = response.elapsed.total_seconds()
return str(response.status_code), f"{seconds:.2f} c"
now = datetime.now().strftime("%Y-%m-%d %H:%M")
with open("sites_log.csv", "a", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
for url in SITES:
status, details = check(url)
writer.writerow([now, url, status, details])
print(f"{status:>6} {details:<16} {url}")
200 0.29 c https://www.python.org
200 0.27 c https://docs.python.org/3/
404 0.29 c https://example.com/no-such-page
ошибка ConnectionError https://no-such-domain.invalid
Второй скрипт получает официальные курсы валют с сайта Банка России. Ответ приходит в XML, его разбирает встроенный модуль xml.etree.ElementTree:
import xml.etree.ElementTree as ET
import requests
URL = "https://www.cbr.ru/scripts/XML_daily.asp"
response = requests.get(URL, timeout=10)
response.raise_for_status()
root = ET.fromstring(response.content)
print("Курсы ЦБ РФ на", root.get("Date"))
for valute in root.iter("Valute"):
code = valute.findtext("CharCode")
if code in {"USD", "EUR", "CNY"}:
nominal = int(valute.findtext("Nominal"))
value = float(valute.findtext("Value").replace(",", "."))
print(f"{nominal} {code} = {value:.2f} ₽")
Курсы ЦБ РФ на 10.10.2026
1 USD = 84.90 ₽
1 EUR = 94.97 ₽
1 CNY = 12.67 ₽
Значения курсов в выводе зависят от даты запуска. Всегда указывайте timeout: без него requests может ждать ответа сервера бесконечно, и скрипт по расписанию «зависнет». Методы, заголовки, JSON-ответы и обработка ошибок разобраны в статье о библиотеке requests, а чтение JSON — в материале о модуле json.
Парсинг страницы (BeautifulSoup)
Если у сайта нет API, данные можно извлечь из HTML. Скрипт берёт оглавление раздела What’s New документации Python:
import requests
from bs4 import BeautifulSoup
URL = "https://docs.python.org/3/whatsnew/index.html"
response = requests.get(URL, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.content, "html.parser")
print(soup.title.get_text(strip=True))
for link in soup.select("div.toctree-wrapper li.toctree-l1 > a")[:5]:
print("-", link.get_text(strip=True), "->", link["href"])
What’s New in Python — Python 3.15.0 documentation
- What’s new in Python 3.15 -> 3.15.html
- What’s new in Python 3.14 -> 3.14.html
- What’s New In Python 3.13 -> 3.13.html
- What’s New In Python 3.12 -> 3.12.html
- What’s New In Python 3.11 -> 3.11.html
Метод select() принимает CSS-селектор — его удобно подобрать в инструментах разработчика браузера (правый клик по элементу → «Просмотреть код»). Мы передаём в BeautifulSoup response.content (байты), а не response.text: тогда кодировка определяется по тегу meta страницы. С response.text на этой странице апостроф превращался в «кракозябры», потому что сервер не указал кодировку в заголовке ответа.
Парсер привязан к вёрстке: сайт поменяет HTML — селектор придётся обновить. Перед парсингом проверьте правила сайта и не отправляйте запросы чаще, чем нужно. Для страниц, которые строятся JavaScript-ом, requests не подойдёт — нужен браузер под управлением Selenium.
Запуск скриптов по расписанию
Автоматизация становится настоящей, когда скрипт запускается сам. Есть два пути: планировщик операционной системы или Python-процесс, который работает постоянно.
Планировщик заданий Windows
Задание создаётся в графическом «Планировщике заданий» (Task Scheduler) или командой schtasks. Указывайте полные пути к интерпретатору и скрипту — у задания нет вашего текущего каталога и активированного окружения:
schtasks /create /tn "Backup project" /sc daily /st 09:00 /tr "C:\scripts\.venv\Scripts\python.exe C:\scripts\backup.py"
Если в пути есть пробелы, путь внутри /tr нужно взять в экранированные кавычки — проще создать задание через графический интерфейс. Учтите, что относительные пути в скрипте (Path("project")) считаются от рабочего каталога, поэтому в скриптах для планировщика надёжнее строить пути от расположения файла: Path(__file__).parent / "project".
cron в Linux и macOS
Откройте таблицу заданий командой crontab -e и добавьте строку: минуты, часы, день месяца, месяц, день недели, команда.
# каждый день в 09:00, вывод и ошибки — в лог
0 9 * * * /home/user/scripts/.venv/bin/python /home/user/scripts/backup.py >> /home/user/scripts/backup.log 2>&1
Лог обязателен: cron работает без терминала, и без перенаправления вывода вы не узнаете, почему скрипт упал.
Библиотека schedule
Если удобнее описать расписание на Python, подойдёт пакет schedule. Скрипт должен работать постоянно — например, на сервере или в отдельном окне терминала:
import time
import schedule
def job():
print("Делаю резервную копию...")
schedule.every().day.at("09:00").do(job)
schedule.every(30).minutes.do(job)
schedule.every().monday.at("08:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)
Закроете окно или перезагрузите компьютер — расписание остановится. Для надёжного ежедневного запуска планировщик ОС лучше; schedule хорош для частых задач внутри уже работающего процесса.
Частые ошибки
FileNotFoundError, хотя файл на месте
Относительный путь считается от текущего рабочего каталога, а не от папки скрипта. Запустили скрипт из другой папки или из планировщика — путь «уехал». Используйте Path(__file__).parent / "имя_файла".
UnicodeDecodeError при чтении CSV
Файл сохранён не в UTF-8 — так часто бывает с CSV, выгруженными из Excel в Windows. Попробуйте encoding="utf-8-sig" (UTF-8 с BOM) или encoding="cp1251". Если Excel показывает ваш CSV «кракозябрами», записывайте его с encoding="utf-8-sig".
ModuleNotFoundError: No module named ‘openpyxl’
Пакет установлен в другой интерпретатор или окружение. В планировщике это особенно частая ошибка: укажите python.exe из того окружения, где стоят пакеты. Разбор всех причин — в статье про ModuleNotFoundError.
Скрипт работает вручную, но не по расписанию
Причины почти всегда три: относительные пути, другой интерпретатор и отсутствие переменных окружения (например, SMTP_PASSWORD). Добавьте логирование в файл и проверьте все три пункта.
Частые вопросы
Что выбрать для Excel: openpyxl или pandas?
openpyxl — когда важен вид файла: стили, ширина столбцов, формулы, закреплённые строки, правка готового шаблона. pandas — когда нужно считать: фильтровать, группировать, объединять таблицы. Их часто сочетают: pandas готовит данные и сохраняет их через df.to_excel() (для .xlsx он пишет файл через openpyxl, а если установлен XlsxWriter — через него), а затем openpyxl оформляет результат.
Как запустить скрипт без чёрного окна консоли в Windows?
Запускайте его через pythonw.exe вместо python.exe — он лежит в той же папке, что и интерпретатор, в том числе в .venv\Scripts. Окно консоли не появится, поэтому вывод print() пропадёт: результаты и ошибки пишите в лог-файл.
Можно ли сделать из скрипта .exe для коллег?
Да, это делает пакет PyInstaller: команда pyinstaller --onefile script.py собирает скрипт вместе с интерпретатором и библиотеками в один исполняемый файл в папке dist. Собирать нужно на той ОС, для которой предназначен файл: .exe для Windows собирают в Windows.
Как автоматизировать работу с Word и PDF?
Для документов Word есть пакет python-docx: он создаёт и редактирует файлы .docx — абзацы, таблицы, стили. Для PDF подходит pypdf: объединение и разделение файлов, поворот страниц, извлечение текста. Ставятся они так же, как openpyxl, через pip.
Можно ли управлять мышью, клавиатурой и браузером?
Да. Пакет pyautogui двигает курсор, кликает и нажимает клавиши — так автоматизируют программы без API, но скрипт ломается при смене разрешения экрана или расположения окон. Действия в браузере надёжнее выполнять через Selenium или Playwright: они находят элементы страницы по селекторам.
Итог
Начните с задачи, которая отнимает у вас время каждую неделю: сортировка файлов, отчёт, рассылка. Возьмите подходящий скрипт, запустите его в режиме проверки, затем поставьте в планировщик. Чтобы увереннее писать такие скрипты, порешайте задачи по Python с решениями, а идеи для более крупных программ ищите в подборке проектов на Python для начинающих. Обзор других полезных пакетов — в статье о популярных библиотеках Python.
- #Python
- #Автоматизация
- #pathlib
- #openpyxl
- #requests
- #Практика