Что можно написать на Python после основ: скрипт, который разбирает папку «Загрузки», отчёт по своим расходам, парсер цен, телеграм-бота, веб-приложение с API и небольшую игру. У каждого из шести проектов есть список библиотек, минимальный рабочий код и вывод, который этот код дал у нас на Python 3.14.3 под Windows 10.
Первый проект на Python лучше брать под задачу, которая у вас уже есть, например разбор своих файлов или расходов. Такой программой вы будете пользоваться сами и быстро заметите, где она ошибается.
| Идея проекта | Уровень | Библиотеки |
|---|---|---|
| Сортировка файлов | новичок | pathlib, shutil (стандартная библиотека) |
| Анализ расходов | новичок | pandas |
| Парсер сайта | новичок | requests, beautifulsoup4 |
| Телеграм-бот | средний | aiogram |
| Веб-приложение | средний | fastapi, uvicorn |
| Игра | средний | pygame-ce |
- С чего начать: окружение и библиотеки
- Проекты на Python для начинающих
- Скрипт автоматизации: разложить файлы по папкам
- Анализ данных: на что ушли деньги
- Парсер: названия и цены со страницы
- Проекты Python для разработчиков среднего уровня
- Телеграм-бот на aiogram
- Веб-приложение на FastAPI
- Игра на pygame-ce
- Где используется Python: идеи для следующих проектов
- Частые вопросы
- Что написать на Python новичку первым?
- Нужно ли знать HTML, чтобы написать парсер?
- Почему бот на aiogram падает с Unauthorized?
С чего начать: окружение и библиотеки
Для каждого проекта заведите отдельный каталог и виртуальное окружение: python -m venv .venv, затем .venv\Scripts\activate в Windows или source .venv/bin/activate в Linux и macOS. Библиотеки ставьте тем интерпретатором, который будет запускать код: python -m pip install requests beautifulsoup4 aiogram fastapi uvicorn pandas pygame-ce. Если импорт после установки всё равно падает, частая причина в том, что pip поставил пакет в другой Python; как это найти, разобрано в статье про ModuleNotFoundError.
Версии библиотек в нашем прогоне: requests 2.34.2, beautifulsoup4 4.15.0, aiogram 3.31.0, fastapi 0.143.0, uvicorn 0.54.0, pandas 3.0.6, pygame-ce 2.5.8. Каждый блок на Python запускается сам по себе, импорты в нём свои.

Проекты на Python для начинающих
Скрипт автоматизации: разложить файлы по папкам
Программа на Python из 31 строки раскладывает файлы по папкам «Картинки», «Документы», «Архивы» по расширению. Для проверки функция make_demo() создаёт рядом со скриптом папку demo_downloads с пятью пустыми файлами, и сортируется только она.
from pathlib import Path
import shutil
FOLDERS = {
".jpg": "Картинки", ".png": "Картинки",
".pdf": "Документы", ".docx": "Документы", ".txt": "Документы",
".zip": "Архивы",
}
def sort_folder(folder: Path) -> None:
for entry in list(folder.iterdir()):
if not entry.is_file():
continue
group = FOLDERS.get(entry.suffix.lower(), "Разное")
target_dir = folder / group
target_dir.mkdir(exist_ok=True)
new_path = target_dir / entry.name
if new_path.exists():
print(f"Пропуск: {entry.name} уже лежит в папке {group}")
continue
shutil.move(entry, new_path)
print(f"{entry.name} -> {group}")
def make_demo() -> Path:
demo = Path("demo_downloads")
demo.mkdir(exist_ok=True)
for name in ["отпуск.JPG", "счёт.pdf", "заметки.txt", "backup.zip", "setup.exe"]:
(demo / name).touch()
return demo
sort_folder(make_demo())
backup.zip -> Архивы
setup.exe -> Разное
заметки.txt -> Документы
отпуск.JPG -> Картинки
счёт.pdf -> Документы
entry.suffix.lower() превращает .JPG в .jpg, иначе фото с камеры ушло бы в «Разное». list(folder.iterdir()) снимает список заранее: код создаёт подпапки, а по документации не определено, попадут ли в перебор элементы, появившиеся во время обхода. Проверка new_path.exists() не даёт затереть одноимённый файл, который уже лежит на месте. Чтобы разобрать настоящие «Загрузки», замените последнюю строку на sort_folder(Path.home() / "Downloads"), и тогда make_demo() не вызывается. Если вы переносили папку загрузок, впишите свой путь.

Анализ данных: на что ушли деньги
Анализ своих данных хорош для новичка тем, что ответ можно проверить в уме. Семь строк расходов вписаны прямо в код, чтобы пример запускался без файла. Библиотека pandas читает таблицу, группирует строки по категориям и считает суммы.
from io import StringIO
import pandas as pd
data = """date,category,amount
2026-09-01,Продукты,1250
2026-09-02,Транспорт,320
2026-09-03,Продукты,890
2026-09-04,Кафе,640
2026-09-05,Транспорт,320
2026-09-06,Продукты,1430
2026-09-07,Кафе,410
"""
expenses = pd.read_csv(StringIO(data), parse_dates=["date"])
by_category = expenses.groupby("category")["amount"].sum().sort_values(ascending=False)
print(by_category)
print("Всего:", expenses["amount"].sum())
category
Продукты 3570
Кафе 1050
Транспорт 640
Name: amount, dtype: int64
Всего: 5260
Для своего файла замените StringIO(data) на путь: pd.read_csv("bank.csv", parse_dates=["date"]). Столбцы в чужой выгрузке называются иначе, сначала посмотрите их через print(expenses.columns). Выгрузку в Windows-1251 с «;» между полями read_csv без параметров не открывает (UnicodeDecodeError), а читает её pd.read_csv("bank.csv", sep=";", encoding="cp1251"). Суммы по категориям просятся в столбчатую диаграмму, примеры есть в статье про графики в Matplotlib.
Парсер: названия и цены со страницы
Парсинг учите на учебном сайте: правила настоящего сайта могут запрещать автоматический сбор данных, а на частые запросы сервер может ответить кодом 429 Too Many Requests. books.toscrape.com сделан для учёбы: вымышленный книжный магазин, который сам просит, чтобы его разбирали скриптами. Библиотека requests скачивает страницу, а BeautifulSoup с помощью CSS-селекторов находит нужные теги: например, article.product_pod означает тег article с классом product_pod.
import requests
from bs4 import BeautifulSoup
URL = "https://books.toscrape.com/"
response = requests.get(URL, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.content, "html.parser")
books = soup.select("article.product_pod")
for book in books[:5]:
title = book.h3.a["title"]
price = book.select_one("p.price_color").text
print(f"{price:>8} {title}")
print("Книг на странице:", len(books))
£51.77 A Light in the Attic
£53.74 Tipping the Velvet
£50.10 Soumission
£47.82 Sharp Objects
£54.23 Sapiens: A Brief History of Humankind
Книг на странице: 20
В BeautifulSoup передаём response.content, то есть байты. С response.text у нас вышли цены вида £51.77. Сервер отдаёт заголовок Content-Type: text/html без кодировки, и requests декодирует страницу как ISO-8859-1, хотя она в UTF-8 и пишет об этом в теге <meta>. Из байтов BeautifulSoup этот тег читает сам.
timeout=10 ставьте в каждый запрос: без него requests ждёт ответа сколько угодно, и зависший сервер подвесит программу. raise_for_status() превращает ответ 404 или 500 в исключение, чтобы вы не разбирали молча страницу с ошибкой.

Ещё несколько простых идей для практики уже разобраны с кодом: генератор паролей, «угадай число» и список дел в файле есть в подборке простых программ на Python, а окно с кнопками на tkinter собрано в статье про калькулятор на Python.
Проекты Python для разработчиков среднего уровня
Телеграм-бот на aiogram
Наш бот считает слова в присланном тексте, а сам подсчёт вынесен в обычную функцию Python count_words_in. aiogram 3 асинхронный: обработчики объявлены через async def, ответ уходит через await. Токен выдаёт @BotFather в Telegram. В код его не пишите: любой, у кого он есть, может управлять вашим ботом. Программа берёт токен из переменной окружения BOT_TOKEN.
import asyncio
import os
from aiogram import Bot, Dispatcher
from aiogram.filters import CommandStart
from aiogram.types import Message
dp = Dispatcher()
@dp.message(CommandStart())
async def start(message: Message) -> None:
await message.answer("Пришлите текст, и я посчитаю в нём слова.")
def count_words_in(text: str) -> int:
return len(text.split())
@dp.message()
async def count_words(message: Message) -> None:
count = count_words_in(message.text or "")
await message.answer(f"Слов в сообщении: {count}")
async def main() -> None:
token = os.getenv("BOT_TOKEN")
if not token:
print("Нет токена: задайте переменную окружения BOT_TOKEN")
return
bot = Bot(token=token)
await dp.start_polling(bot)
asyncio.run(main())
Без переменной программа печатает Нет токена: задайте переменную окружения BOT_TOKEN и завершается. С выдуманным токеном Telegram отказывает, и traceback заканчивается строкой aiogram.exceptions.TelegramUnauthorizedError: Telegram server says - Unauthorized. С рабочим токеном бота мы не запускали, код собран по документации aiogram. В PowerShell переменную задают так: $env:BOT_TOKEN="ваш_токен", затем в том же окне запускают файл.
message.text or "" нужен потому, что поле text есть только у текстовых сообщений: у фото подпись лежит в caption, а message.text равен None.

Веб-приложение на FastAPI
Разработку веб-приложения на Python удобно начинать с маленького API: два адреса, JSON на входе и на выходе. Фреймворк FastAPI сам проверяет входные данные по модели Pydantic и строит страницу документации. Flask подходит для той же задачи, его пример мы разбирали в статье что такое фреймворк.
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
notes: list[str] = []
class Note(BaseModel):
text: str
@app.post("/notes")
def add_note(note: Note) -> dict:
notes.append(note.text)
return {"id": len(notes), "text": note.text}
@app.get("/notes")
def list_notes() -> dict:
return {"count": len(notes), "notes": notes}
Сохраните код как notes_app.py и запустите сервер из той же папки командой python -m uvicorn notes_app:app. Он напишет Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit). Во втором терминале отправим заметку и запросим список. Команды для Git Bash, Linux и macOS:
curl -X POST http://127.0.0.1:8000/notes -H "Content-Type: application/json" -d '{"text": "buy milk"}'; echo
curl http://127.0.0.1:8000/notes; echo
{"id":1,"text":"buy milk"}
{"count":1,"notes":["buy milk"]}
В PowerShell 5.1, который входит в Windows 10, curl означает Invoke-WebRequest, и первая команда в таком виде падает с ошибкой привязки параметра Headers. Там тот же запрос отправляет Invoke-RestMethod:
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8000/notes -ContentType "application/json" -Body '{"text": "buy milk"}'
Invoke-RestMethod http://127.0.0.1:8000/notes
id text
-- ----
1 buy milk
count notes
----- -----
1 {buy milk}
Дойдёт ли кириллица в теле запроса, зависит от среды. В Git Bash под Windows она сломалась прямо в -d (There was an error parsing the body), а из файла в UTF-8 (-d @note.json) прошла. В PowerShell 5.1 пишите -ContentType "application/json; charset=utf-8": без charset сервер сохранил вместо букв знаки вопроса. Заметка с charset сохраняется верно, но в ответе PowerShell 5.1 всё равно показывает её кракозябрами. Запросы можно отправлять и со страницы /docs, которую FastAPI строит сам.
На запрос с пустым телом {} сервер вернул код 422 и "msg":"Field required": поля text нет, и FastAPI отклонил запрос, не вызывая нашу функцию. Заметки лежат в списке в памяти и пропадают при перезапуске сервера; следующий шаг проекта: хранить их в SQLite.

Игра на pygame-ce
Разработка игр учит тому, чего нет в скриптах: программа крутится в цикле, читает клавиши, двигает объекты и перерисовывает кадр. Здесь платформа внизу ловит падающий квадрат, управление стрелками.
На Python 3.14 команда pip install pygame у нас не сработала: у последней версии pygame 2.6.1 нет готовых сборок под 3.14, и pip падает с Failed to build 'pygame'. Ставьте pygame-ce, форк от бывших разработчиков pygame. Импорт тот же: import pygame.
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 640))
pygame.display.set_caption("Поймай квадрат")
clock = pygame.time.Clock()
paddle = pygame.Rect(200, 600, 80, 15)
target = pygame.Rect(random.randint(0, 460), 0, 20, 20)
score = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
paddle.x -= 6
if keys[pygame.K_RIGHT]:
paddle.x += 6
paddle.clamp_ip(screen.get_rect())
target.y += 4
if target.colliderect(paddle):
score += 1
target.topleft = (random.randint(0, 460), 0)
elif target.top > screen.get_height():
print("Промах, счёт:", score)
target.topleft = (random.randint(0, 460), 0)
screen.fill((20, 24, 40))
pygame.draw.rect(screen, (240, 200, 60), target)
pygame.draw.rect(screen, (90, 200, 250), paddle)
pygame.display.flip()
clock.tick(60)
pygame.quit()
print("Игра окончена, пойман квадратов:", score)

pygame-ce 2.5.8 (SDL 2.32.10, Python 3.14.3)
Промах, счёт: 1
Игра окончена, пойман квадратов: 1
Первую строку печатает библиотека при импорте. Квадрат появляется в случайном месте, поэтому счёт у вас будет другим. clock.tick(60) не даёт циклу крутиться чаще 60 раз в секунду. Квадрат сдвигается на 4 пикселя за кадр, это 240 пикселей в секунду, и окно высотой 640 пикселей он проходит примерно за 2,7 секунды. clamp_ip сдвигает платформу обратно внутрь окна, меняя сам paddle.
Где используется Python: идеи для следующих проектов
Следующий проект можно выбрать по области применения Python:
| Область | Что можно написать | Библиотеки |
|---|---|---|
| Веб-разработка | серверную часть сайта, API для бота | Django, Flask, FastAPI |
| Data science и аналитика | отчёты, анализ таблиц, графики | pandas, NumPy, matplotlib |
| Машинное обучение | прогноз по прошлым данным, нейросети | scikit-learn, PyTorch, TensorFlow |
| Компьютерное зрение | обработку изображений | OpenCV (пакет opencv-python) |
| Обработка естественного языка | разбиение текста на слова и предложения, классификацию отзывов | NLTK |
| Тестирование | автотесты для своего кода | pytest |
| Десктопные и мобильные приложения | окно для своего скрипта, приложение для компьютера и телефона на одном коде | tkinter, Kivy |
На старте мы бы не распылялись: выберите одну область из таблицы и доведите в ней до рабочего состояния один проект на Python.
Частые вопросы
Что написать на Python новичку первым?
Из шести проектов статьи без установки библиотек работает только сортировка файлов: ей хватает стандартной библиотеки Python, а результат видно в папке. Если и она пока сложна, начните с маленьких программ на условия и циклы.
Нужно ли знать HTML, чтобы написать парсер?
Нужен базовый уровень: теги, атрибуты class и id, вложенность. Этого хватает, чтобы в инструментах разработчика браузера найти нужный блок и записать селектор вроде article.product_pod.
Почему бот на aiogram падает с Unauthorized?
Telegram не принял токен. Скопируйте его из @BotFather заново целиком, без пробелов и кавычек. Если в @BotFather выпускали новый токен командой /token, берите последний.








