Image

Я оплачиваю квартиру за счет Python-инструмента, который собрал за выходные

Как превратить микро-скрипт для автоматизации в коммерческий продукт: библиотеки, чистый ООП и немного C++ для скорости

71c1a4c79c30a5b361b0478d4bd9342d

Всем привет! Снова отыскала интересную статью на Medium, интересно услышать ваше мнение 🙂

Я создал этот инструмент, потому что устал от одних и тех же скучных кликов каждую неделю. Мне нужен был инструмент, который: отслеживает папку, извлекает данные из PDF, обогащает их, отправляет отчеты и, в идеале, позволяет выставлять кому-то счет за сэкономленное время. Два выходных, несколько библиотек и пачка кофе – и у меня был продукт, за который люди действительно платили.

Ниже я покажу точный технологический стек, архитектуру, методы монетизации и паттерны кода, которые я использовал. Вас ждет практический код, ООП-структура и один небольшой трюк с C++, когда чистого Python уже не хватало.

1. Выбирайте маленькую, но болезненную задачу

Большинство проектов по автоматизации умирают, потому что пытаются решить слишком много. Вместо этого выберите одну повторяющуюся «боль» с измеримым ROI. Моя проблема была такой:

  1. Клиент ежедневно присылает счета (инвойсы) в виде разрозненных PDF.

  2. Я вручную открываю их, извлекаю поставщика, дату, сумму и заношу в Google Sheet.

  3. Трата: ≈ 20 минут в день, или ≈ 6 часов в месяц.

  4. Цель: Свести это к нулю человеко-минут и предложить как платную услугу.

2. Быстрый MVP: Наблюдатель за файлами + PDF-экстрактор

Начните с малого: отслеживайте папку, обнаруживайте новый PDF, извлекайте текст. Используем watchdog и PyMuPDF (fitz).

pip install watchdog pymupdf # file_watcher.py import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler import fitz # pymupdf class PDFHandler(FileSystemEventHandler): «»»Обработчик событий файловой системы.»»» def on_created(self, event): if event.src_path.lower().endswith(«.pdf»): print(f»[+] Новый PDF: {event.src_path}») text = extract_text(event.src_path) print(text[:200], «…n») # быстрый превью def extract_text(path: str) -> str: «»»Извлечение текстового слоя из PDF.»»» doc = fitz.open(path) pages = [page.get_text() for page in doc] doc.close() return «n».join(pages) if __name__ == «__main__»: observer = Observer() handler = PDFHandler() # Следим за папкой ‘inbox’ observer.schedule(handler, path=»./inbox», recursive=False) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()

Этот единственный скрипт сократил мою ежедневную работу до 5 минут – в основном на проверку

3. Повышаем отказоустойчивость: OCR + текстовый фолбэк

Некоторые PDF – это просто сканированные изображения. Добавим фолбэк с помощью pytesseract.

pip install pytesseract pillow # Внимание: tesseract должен быть установлен в системе (apt / brew / choco)from PIL import Image import pytesseract import fitz def extract_text_with_ocr(path: str) -> str: «»»Извлечение текста: сначала текстовый слой, затем OCR для сканов.»»» doc = fitz.open(path) aggregated = [] for page in doc: text = page.get_text() if text.strip(): # Если есть текстовый слой, используем его aggregated.append(text) else: # Иначе делаем скриншот страницы и прогоняем через Tesseract pix = page.get_pixmap(dpi=200) img = Image.frombytes(«RGB», [pix.width, pix.height], pix.samples) aggregated.append(pytesseract.image_to_string(img)) doc.close() return «n».join(aggregated)

Такой гибридный подход (текстовый слой → OCR) сделал инструмент надежным для 95% входящих счетов.

4. Архитектура: ООП для модульности (Пайплайн)

Если вы хотите превратить это в продукт, пайплайн должен быть модульным. Каждый шаг – это класс: Loader → Parser → Enricher → Sink. Это позволяет легко менять хранилище (Google Sheets, СУБД, вебхук) без переписывания логики.

# pipeline.py from abc import ABC, abstractmethod from typing import Dict class Step(ABC): «»»Абстрактный класс для шага обработки.»»» @abstractmethod def run(self, data: Dict) -> Dict: pass class Loader(Step): «»»Загружает данные (извлекает текст).»»» def __init__(self, path): self.path = path def run(self, data): data[‘text’] = extract_text_with_ocr(self.path) return data class Parser(Step): «»»Извлекает ключевые поля (сумма, дата, поставщик).»»» def run(self, data): text = data[‘text’] # Здесь будет реальная логика парсинга (regex/NLP) data[‘vendor’] = find_vendor(text) data[‘amount’] = find_amount(text) return data class Sink(Step): «»»Выгружает данные в целевую систему (например, Google Sheets).»»» def run(self, data): push_to_google_sheet(data) return data class Pipeline: «»»Сборщик и исполнитель пайплайна.»»» def __init__(self, steps: list[Step]): self.steps = steps def execute(self, initial: Dict): data = initial for step in self.steps: data = step.run(data) return data

Этот паттерн масштабируем: легко добавить ClassifierStep для определения языка или TranslatorStep для неанглоязычных документов.

5. Извлечение и обогащение: Regex, а затем ML

Начинайте с детерминированного парсинга (regex). Если счета сложные, переходите к ML-моделям (например, spacy или layout-parser). Пример для извлечения суммы:

import re # Регулярное выражение для поиска сумм с валютой (USD/EUR/$, с запятыми/точками) AMOUNT_RE = re.compile(r»(?<!d)(?:USD|EUR|$)?s?([d]{1,3}(?:,d{3})*(?:.d{2})?)b») def find_amount(text: str) -> float | None: text = text.replace(«n», » «) m = AMOUNT_RE.search(text) if m: # Удаляем разделители тысяч, чтобы корректно преобразовать в float s = m.group(1).replace(‘,’, ») return float(s) return None

Для повышения надежности используйте spacy + кастомный NER или библиотеки вроде layout-parser для пространственного обнаружения полей.

6. Веб-автоматизация: Playwright для загрузки

Если счета находятся за веб-дашбордами, автоматизируйте их загрузку с помощью Playwright.

pip install playwright playwright installfrom playwright.sync_api import sync_playwright def login_and_download(url, user, password, download_path): «»»Автоматически логинится и скачивает файл.»»» with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page() page.goto(url) # Автоматическое заполнение полей page.fill(‘#username’, user) page.fill(‘#password’, password) page.click(‘#login’) # Ожидание элемента и скачивание page.wait_for_selector(‘a.download’) with page.expect_download() as download_info: page.click(‘a.download’) download = download_info.value download.save_as(download_path) browser.close()

Это позволяет вашему сервису самостоятельно собирать исходные PDF – критически важно для подписки, где система должна сама получать документы клиента каждое утро.

7. Распространение: CLI с Typer / Click

Для удобства использования оберните функциональность в CLI-интерфейс с помощью Typer (или Click). Это позволяет не-разработчикам запускать его локально или упрощает развертывание на серверах.

pip install typer# cli.py import typer from pipeline import Pipeline, Loader, Parser, Sink app = typer.Typer() @app.command() def process(path: str): «»»Обрабатывает один файл по указанному пути.»»» steps = [Loader(path), Parser(), Sink()] p = Pipeline(steps) p.execute({}) typer.echo(f»Файл {path} успешно обработан!») if __name__ == «__main__»: app()

Опубликуйте проект на PyPI (с setup.py или pyproject.toml) или упакуйте в Docker-образ.

8. Оптимизация: Ускорение с C++ (pybind11)

Для тяжелой обработки изображений или масштабного пре-процессинга для OCR Python может стать узким местом. У меня был шаг (кастомное преобразование изображения), который должен был обрабатывать тысячи страниц в день.

Я переписал этот единственный шаг на C++ и сделал его доступным из Python через pybind11 (можно использовать и Cython).

Суть подхода:

  1. Написать тяжелую функцию на C++.

  2. Обернуть ее с помощью pybind11 (для Python-интерфейса).

  3. Импортировать скомпилированный модуль в Python как обычную библиотеку.

Этот микро-рефакторинг сократил время выполнения шага со ≈ 120 мс/страница до ≈ 10 мс/страница. Знайте, где замедляет Python, и изолируйте это место.

9. Масштабирование: Воркеры с Celery + Redis

При росте числа пользователей обрабатывайте задачи в очередях воркеров, чтобы не блокировать основной поток.

pip install celery redis# tasks.py from celery import Celery from pipeline import Pipeline, Loader, Parser, Sink # Настройка Celery с Redis в качестве брокера app = Celery(‘tasks’, broker=’redis://localhost:6379/0′) @app.task def process_file(path): «»»Задача Celery для асинхронной обработки.»»» steps = [Loader(path), Parser(), Sink()] Pipeline(steps).execute({})

10. Монетизация: От фриланса до SaaS

Как я превратил это в деньги:

  1. Фриланс-контракты (ранняя выручка): Предложил автоматизацию обработки счетов нескольким местным клиентам. Быстрые победы, минимальная поддержка.

  2. Цена за документ (Volume pricing): Тарификация за обработанный счет (например, $0.10–$0.50). Отлично для клиентов с большим объемом.

  3. Ежемесячная подписка (SaaS): Хостинг сервиса, автоматический инжест (через Playwright или SFTP) и оплата за удобство + SLA.

  4. White-label / Enterprise: Интеграция в существующую бухгалтерскую платформу.

Ключевые тактики, которые помогли конвертировать лиды:

  • Двухнедельный бесплатный пробный период: Обработка первых 50 счетов клиента бесплатно.

  • Прозрачный отчет о точности: Показываем, что было спарсено машиной, а что пришлось править вручную.

  • Human-in-the-loop (человек в контуре): Предлагаем ручную коррекцию для результатов с низкой уверенностью (дополнительный доход).

11. Скелет репозитория (стартовая точка)

Используйте эту структуру в качестве основы для любого серьезного проекта по автоматизации:

invoice-automator/ ├─ pyproject.toml ├─ README.md ├─ src/ │ ├─ automator/ │ │ ├─ __init__.py │ │ ├─ cli.py # Точка входа CLI │ │ ├─ pipeline.py # Основная логика ООП-пайплайна │ │ ├─ loaders.py # Классы Loader (файл, загрузка, Playwright) │ │ ├─ parsers.py # Regex / NLP парсеры │ │ ├─ ocr.py # OCR-утилиты и фоллбэк │ │ ├─ enrichers.py # Нормализация валют, поиск поставщиков │ │ ├─ sinks.py # Google Sheets / DB / Webhook │ │ └─ utils.py │ └─ tests/ │ ├─ test_parsers.py │ └─ test_pipeline.py ├─ docker/ │ ├─ Dockerfile │ └─ prod-compose.yml └─ infra/ └─ celery_worker.yml # Конфигурация воркера

Какие еще рутинные задачи вы хотели бы автоматизировать с помощью этого архитектурного шаблона?

Источник: habr.com

Каталог бесплатных опенсорс-решений, которые можно развернуть локально и забыть о подписках

галерея

СОСТОЯЛОСЬ ЗАСЕДАНИЕ МЕТОДИЧЕСКОГО СОВЕТА, ПОСВЯЩЕННОЕ ПОКОЛЕНИЮ «РОЖДЕННЫХ ЦИФРОВЫМИ»
СОСТОЯЛОСЬ ЗАСЕДАНИЕ МЕТОДИЧЕСКОГО СОВЕТА, ПОСВЯЩЕННОЕ ПОКОЛЕНИЮ «РОЖДЕННЫХ ЦИФРОВЫМИ»
Биофизический мир внутри переполненной клетки
Появились новые доказательства того, как одиночество влияет на память в пожилом возрасте.
NVIDIA ReSTIR PR Enhanced повышает производительность трассировки пути в три раза
«Слишком сложно и дорого»: могли ли американцы сымитировать полет к Луне с помощью ИИ
«Слишком сложно и дорого»: могли ли американцы сымитировать полет к Луне с помощью ИИ
L-эрготиоин: антиоксидант, содержащийся в грибах, может воздействовать на клетки матки, облегчая менструальные боли.
L-эрготиоин: антиоксидант, содержащийся в грибах, может воздействовать на клетки матки, облегчая менструальные боли.
Image Not Found
СОСТОЯЛОСЬ ЗАСЕДАНИЕ МЕТОДИЧЕСКОГО СОВЕТА, ПОСВЯЩЕННОЕ ПОКОЛЕНИЮ «РОЖДЕННЫХ ЦИФРОВЫМИ»

СОСТОЯЛОСЬ ЗАСЕДАНИЕ МЕТОДИЧЕСКОГО СОВЕТА, ПОСВЯЩЕННОЕ ПОКОЛЕНИЮ «РОЖДЕННЫХ ЦИФРОВЫМИ»

19 февраля 2026 года прошло заседание Методического совета, посвященное теме «“Рожденные цифровыми” как субъекты учения: специфика и ее учет в преподавании». В мероприятии участвовали члены Методсовета, проректор по учебной работе, начальник УМУ, а также коллеги с филологического,…

Апр 21, 2026
СОСТОЯЛОСЬ ЗАСЕДАНИЕ МЕТОДИЧЕСКОГО СОВЕТА, ПОСВЯЩЕННОЕ ПОКОЛЕНИЮ «РОЖДЕННЫХ ЦИФРОВЫМИ»

СОСТОЯЛОСЬ ЗАСЕДАНИЕ МЕТОДИЧЕСКОГО СОВЕТА, ПОСВЯЩЕННОЕ ПОКОЛЕНИЮ «РОЖДЕННЫХ ЦИФРОВЫМИ»

19 февраля 2026 года прошло заседание Методического совета, посвященное теме «“Рожденные цифровыми” как субъекты учения: специфика и ее учет в преподавании». В мероприятии участвовали члены Методсовета, проректор по учебной работе, начальник УМУ, а также коллеги с филологического,…

Апр 21, 2026
NVIDIA ReSTIR PR Enhanced повышает производительность трассировки пути в три раза

NVIDIA ReSTIR PR Enhanced повышает производительность трассировки пути в три раза

Исследователи NVIDIA пытаются найти способы повысить производительность ресурсозатратной трассировки пути, которая по сей день остаётся очень тяжёлой нагрузкой даже для лучших игровых видеокарт. К счастью, им удалось найти один из вариантов, как можно не только поднять FPS,…

Апр 21, 2026
Многоразовая ракета New Glenn компании Blue Origin успешно приземлилась, но доставка полезной нагрузки не удалась.

Многоразовая ракета New Glenn компании Blue Origin успешно приземлилась, но доставка полезной нагрузки не удалась.

Однако ей не удалось доставить полезную нагрузку с космической вышки сотовой связи. Теренс О'Брайен, редактор раздела «Выходные». Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Апр 20, 2026

Впишите свой почтовый адрес и мы будем присылать вам на почту самые свежие новости в числе самых первых