ETL-проект для личных финансов: парсит банковские выписки из нескольких источников, нормализует транзакции, определяет тип операций, уточняет категории расходов и сохраняет итоговую таблицу в CSV.
Проект вырос из реальной бытовой задачи по учёту семейных финансов и со временем превратился в переиспользуемый пайплайн обработки полуструктурированных финансовых данных.
- Решает прикладную задачу: собирает операции из разных банков и форматов выписок в одну чистую таблицу.
- Работает с “грязным” реальным вводом: PDF-выписки, CSV-экспорты, неоднотипные описания операций и различающиеся банковские форматы.
- Показывает полный цикл работы с данными: парсинг, очистка, обогащение по правилам и выгрузка в отчёт.
- Парсинг выписок из:
- PDF по карте Сбера
- PDF по сберегательным счетам и вкладам Сбера
- CSV-экспортов Т-Банка
- PDF-выписок Ozon Банка
- Определение направления и типа транзакции
- Нормализация категорий по YAML-правилам
- Фильтрация внутренних переводов между своими счетами
- Сохранение итогового датасета в CSV
- Готовый демо-набор на вымышленных данных (
demo_data/)
- Python
- pandas
- pdfplumber
- PyYAML
- Tableau
flowchart LR
A["Банковские выписки (PDF / CSV)"] --> B["Слой парсинга (кастомные парсеры)"]
B --> C["Нормализованная таблица транзакций"]
C --> D["Определение типа (доход / расход / внутренний / возврат)"]
D --> E["Уточнение категорий (YAML-правила)"]
E --> F["Финальный подготовленный датасет"]
F --> G["CSV с результатом (<набор>/output/)"]
.
├── configs/
│ ├── types.sample.yaml # демо-правила типов под demo_data/
│ └── categories.sample.yaml # демо-правила категорий под demo_data/
├── parsers/
│ ├── sber.py # парсеры PDF-выписок Сбера
│ ├── tbank.py # парсер CSV из Т-Банка
│ ├── ozon.py # парсер PDF-выписок Ozon
│ └── utils.py # regex-шаблоны, нормализация текста, валидация схемы
├── pipeline/
│ ├── parsing.py # обход папки и маршрутизация файлов
│ ├── typification.py # определение типа операции и возвратов
│ ├── categorization.py # присвоение категорий по правилам
│ ├── coverage.py # проверка покрытия и заготовки правил
│ ├── logger.py # настройка логирования
│ └── utils.py # загрузка YAML-конфигов
├── demo_data/ # вымышленный набор для демо
│ ├── input/ # игрушечные выписки
│ └── output/ # результат демо-прогона
├── data/ # реальные данные (в .gitignore): input/ + output/
├── run_pipeline.py # CLI-точка входа
└── requirements.txt
Реальные configs/types.yaml / configs/categories.yaml содержат личные правила и в git не попадают.
Проект обходит папку с выписками, определяет источник по расширению файла и префиксу имени, а затем передаёт файл в нужный парсер.
Основные точки входа:
После парсинга все операции приводятся к единой схеме:
sourcedatedirectionraw_amountraw_categoryraw_description
Дальше пайплайн:
- определяет тип операции (
доход,расход,внутренний,возврат) - уточняет категории на основе YAML-правил
- удаляет переводы между собственными счетами
Ключевая логика:
Правила покрывают не все транзакции. Категория при непокрытии берётся из сырой банковской (raw_category заполнена всегда), а вот подкатегория без правила остаётся пустой — при добавлении новых выписок копятся недоразмеченные операции. Поэтому после категоризации пайплайн проверяет покрытие по подкатегориям и логирует метрику ([COVERAGE] .../... (%)).
Если покрытие неполное, пайплайн:
- пишет
output/suggested_rules.yaml— готовые YAML-заготовки правил (по одному широкому правилу на банковскую категорию), под вставку вcategories.yaml; - при ручном запуске в терминале — приостанавливается и ждёт: правишь правила, жмёшь Enter → пересчёт только категоризации (без повторного парсинга PDF, ~1с), новая метрика; цикл до 100% или до
q; - при автоматическом запуске (не терминал) — интерактив пропускается, недоразмеченные получают подкатегорию «Прочее», прогон идёт дальше.
Итог всегда полный: непокрытые подкатегории заполняются значением «Прочее». Логика — pipeline/coverage.py.
Финальный очищенный датасет сохраняется в CSV рядом с входными данными — в <data_набор>/output/transactions.csv (перезаписывается при каждом прогоне). Дальше его удобно подхватывать в BI-инструментах.
Итоговый датасет визуализируется в Tableau.
Дашборд собран на вымышленном наборе demo_data/.
🔗 Интерактивная версия: Finance Flow на Tableau Public
Интерактив — фильтры по году/месяцу и drill-down по категориям:
Фильтрация по месяцу:
Drill-down: клик по категории фильтрует весь дашборд:
Детализация по выбранной категории:
Сам файл
.twbxв репозиторий не коммитится (содержит экстракт данных) — он остаётся локально, а в портфолио ведёт ссылка на Tableau Public.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtПоложите выписки в папку data/input/.
Сейчас поддерживаются такие шаблоны имён:
sber*.pdftbank*.csvozon*.pdf
Реальные configs/types.yaml и configs/categories.yaml лежат вне git. В качестве отправной точки возьмите демо-конфиги и отредактируйте их под свои банки и категории.
А для прогона на демо-данных конфиги уже готовы — configs/types.sample.yaml и configs/categories.sample.yaml, копировать ничего не нужно.
Запуск из командной строки:
python run_pipeline.pyПо умолчанию результат сохранится рядом с входными данными — в data/output/transactions.csv и будет перезаписываться при каждом прогоне. Каждый датасет самодостаточен: <папка>/input/ для выписок, <папка>/output/ для результата.
Готовый пример на вымышленных данных лежит в demo_data/:
python run_pipeline.py --data-dir demo_data \
--types-config configs/types.sample.yaml \
--categories-config configs/categories.sample.yaml- Разработку кастомных парсеров для полуструктурированных документов
- Проектирование слоя нормализации для неоднородных источников данных
- Правила категоризации и контроля качества данных
- Автоматизацию рутинной личной аналитики
- Интеграцию Python-пайплайна с инструментами отчётности
Репозиторий подготовлен для публичного GitHub-портфолио. Реальные банковские выписки, Excel-файлы и ключи доступа не должны попадать в git. Папка data/ (входные данные и выгрузки) и личные configs/*.yaml целиком исключены через .gitignore; в git попадают только вымышленный набор demo_data/, и демо-конфиги configs/*.sample.yaml.



