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

✅ Найденные теги: «Я, новости

ОСТАВЬТЕ СВОЙ КОММЕНТАРИЙ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

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

галерея

Фото сгенерированных лиц: исследование показывает, что люди не могут отличить настоящие лица от сгенерированных
Нейросети построили капитализм за трое суток: 100 агентов Claude заперли…
Скетч: цифровой осьминог и виртуальный мир внутри компьютера с человечком.
Сцена с жестами пальцами, где один жест символизирует "VPN", а другой "KHP".
‼️Paramount купила Warner Bros. Discovery — сумма сделки составила безумные…
Скриншот репозитория GitHub "Claude Scientific Skills" AI для научных исследований.
Структура эффективного запроса Claude с элементами задачи, контекста и референса.
Эскиз и готовая веб-страница платформы для AI-дизайна в современном темном режиме.
ideipro logotyp
Image Not Found
Звёздное небо с галактиками и туманностями, космос, Вселенная, астрофотография.

Система оповещения обсерватории Рубина отправила 800 000 сигналов в первую ночь наблюдений.

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

Мар 2, 2026
Женщина с длинными тёмными волосами в синем свете, нейтральный фон.

Расследование в отношении 61-фунтовой машины, которая «пожирает» пластик и выплевывает кирпичи.

Обзор компактного пресса для мягкого пластика Clear Drop — и что будет дальше. Шон Холлистер, старший редактор Публикации этого автора будут добавляться в вашу ежедневную рассылку по электронной почте и в ленту новостей на главной странице вашего…

Мар 2, 2026
Черный углеродное волокно с текстурой плетения, отражающий свет.

Материал будущего: как работает «бессмертный» композит

Учёные из Университета штата Северная Каролина представили композит нового поколения, способный самостоятельно восстанавливаться после серьёзных повреждений.  Речь идёт о модифицированном армированном волокном полимере (FRP), который не просто сохраняет прочность при малом весе, но и способен «залечивать» внутренние…

Мар 2, 2026
Круглый экран с изображением замка и горы, рядом электронная плата.

Круглый дисплей Waveshare для креативных проектов

Круглый 7-дюймовый сенсорный дисплей от Waveshare создан для разработчиков и дизайнеров, которым нужен нестандартный экран.  Это IPS-панель с разрешением 1 080×1 080 пикселей, поддержкой 10-точечного ёмкостного сенсора, оптической склейкой и защитным закалённым стеклом, выполненная в круглом форм-факторе.…

Мар 2, 2026

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