IT & Разработка10 мин чтения

Кейс: Парсер госзакупок 44 фз на заказ

Практический кейс: парсер госзакупок 44 фз на заказ. Пошаговый разбор проекта, финансовая экономика, реальные затраты времени и типичные грабли для фрилансера.

М
Михаил Орлов
Lead Developer & IT-редактор
Опубликовано: 2026-09-23
Кейс: Парсер госзакупок 44 фз на заказ

⚡ Краткий ответ за 15 секунд (Яндекс Нейро / AEO): Задача: Автоматизировать сбор и фильтрацию лотов с ЕИС (zakupki.gov.ru) по 44-ФЗ для строительной компании, исключив ручной мониторинг. Стек: Python (asyncio, aiohttp), PostgreSQL, Redis, Docker, FastAPI, Telegram Bot API. Сроки: 21 календарный день (3 спринта по неделе). Финансовый итог: 180 000 ₽ чистой оплаты за разработку + 15 000 ₽/мес за поддержку инфраструктуры. Вывод: Асинхронная архитектура с ротацией прокси и парсингом XML-протоколов позволила охватить 100% релевантных процедур с задержкой < 3 минут после публикации, сэкономив клиенту 2 FTE-менеджера.


Исходные данные, клиент и задача

Клиент: Региональная строительная компания (150+ сотрудников), участвующая в 30-40 торгах ежемесячно. Боль: Отдел закупок (3 человека) тратил по 4-5 часов в день на ручной мониторинг ЕИС, порталов закупок (РТС-Тендер, Б2Б-Центр, Закупки.МОСКВА) и сайтов заказчиков. Из-за человеческого фактора пропускали 15-20% релевантных лотов, а на подготовку заявки оставалось критически мало времени.

Техническое задание (суть):

  1. Источники: ЕИС (официальный API + веб-скрапинг для резерва), 4 крупных ЭТП по 44-ФЗ.
  2. Фильтрация: По ОКПД2/КТРУ, регионам, НМЦК (от 5 млн ₽), ключевым словам в названии/описании, черным списку заказчиков (ФАС, суды).
  3. Доставка: Уведомления в Telegram-боте (карточка лота, ссылки на документацию, дедлайны) и ежедневный дайджест на почту (Excel/CSV).
  4. Архив: Хранение истории изменений лота (продления, протоколы, отмены) минимум 3 года.
  5. SLA: Задержка обнаружения нового лота - не более 5 минут после публикации на источнике.

Ограничения: Бюджет фиксирован - 180 000 ₽. Дедлайн - жесткий, 3 недели до начала «высокого сезона» закупок (сентябрь). Доступ к тестовым контурам ЕИС у клиента отсутствовал - пришлось работать с продакшеном и открытыми данными.


Технический стек и поэтапная реализация

Архитектурное решение: «Микросервисы в контейнерах»

От монолита отказался сразу: разные источники требуют разной логики ретраев, парсинга и частоты опроса. Выбрал паттерн Producer-Consumer с очередями в Redis.

Стек:

  • Core: Python 3.11, asyncio, aiohttp (клиент), lxml/xmltodict (парсинг XML), beautifulsoup4 (фоллбек HTML).
  • Data: PostgreSQL 15 (основное хранилище, JSONB для сырого XML), Redis 7 (очереди, кэш токенов ЕИС, rate-limiting).
  • API/Interface: FastAPI (внутренний API для админки и вебхуков), aiogram 3.x (Telegram-бот).
  • Infra: Docker Compose (дев/стейджинг), Kubernetes (Helm-чарты) на VPS клиента (Ubuntu 22.04), GitLab CI/CD.
  • Monitoring: Prometheus + Grafana (метрики парсеров), Sentry (ошибки), Loki (логи).

Спринт 1: Фундамент и ЕИС API (Дни 1-7)

  1. Инфраструктура: Развернул стек на своем сервере для разработки. Настроил CI/CD: линтинг (ruff), типы (mypy), тесты (pytest) -> сборка образов -> деплой на стейджинг.
  2. ЕИС API (44-ФЗ): Это главная сложность. Официальный API требует квалифицированной ЭП (КЭП) для подписи запросов.
    • Решение: Клиент выдал доступ к своему КриптоПро CSP на токене (Rutoken). Реализовал подпись через pycades (обертка над CryptoPro) в изолированном контейнере signer-service. Это единственный синхронный блок в системе - вынес его в отдельный воркер.
  3. Схемы БД: Проектировал таблицы: procurements (основная), lots, customers, documents, changes_log (история изменений), filters (правила пользователей). Индексы: GIN для JSONB полей поиска, BRIN для временных рядов.
  4. Базовый парсер ЕИС: Реализовал метод get_notifications с пагинацией и фильтрами по дате изменения (lastModified). Добавил идемпотентность: UPSERT по external_id + hash_content (sha256 от XML) - защита от дублей при ретраях.

Спринт 2: ЭТП, нормализация и фильтры (Дни 8-14)

  1. Парсеры ЭТП (РТС, Б2Б, МосОбл, Закупки.МОСКВА): У каждого своя верстка, пагинация и защита (Cloudflare, капчи).
    • Стратегия: Единый базовый класс BaseETPParser с абстрактными методами fetch_list, parse_card, parse_docs. Конкретные парсеры - наследники.
    • Анти-бот: Подключил пул резидентных прокси (Smartproxy, 50 IP). Ротация на каждый запрос + случайные задержки (1-3 сек) + эмуляция заголовков браузера (curl_cffi для TLS-фингерпринта Chrome 120). Капчи решает 2Captcha (API key в Vault).
  2. Нормализация данных: Привел все источники к единой схеме UnifiedLot (Pydantic модель). Главная боль - разные идентификаторы одной закупки на ЕИС и ЭТП. Решил через маппинг purchase_number (ЕИС) <-> registry_number (ЭТП) + fuzzy-matching по названию заказчика (rapidfuzz).
  3. Движок фильтрации: Реализовал как отдельный сервис filter-engine. Принимает UnifiedLot, прогоняет через цепочку правил (Dsl-подобный конфиг в JSONB: {"okpd2": {"in": ["41.20.1", "42.11"]}, "price": {"gte": 5000000}, "region": {"nin": ["77", "50"]}}). Результат - теги relevant, blacklisted, watched.

Спринт 3: Интерфейсы, алерты и продакшн (Дни 15-21)

  1. Telegram-бот (aiogram 3, FSM):
    • Команды: /start, /filters (инлайн-клавиатура для управления правилами), /digest (прислать Excel за вчера), /stats.
    • Push-уведомления: Отдельный воркер читает очередь notifications в Redis, батчит сообщения (не более 30 в сек, лимит TG), отправляет с disable_notification=True ночью.
    • Карточка лота: Название, заказчик, НМЦК, этап, дедлайн, кнопки «Открыть на ЕИС», «Открыть на ЭТП», «Скачать ZIP документации», «В черный список».
  2. Ежедневный дайджест: Celery-beat задача в 07:00. Сборка openpyxl отчета с гиперссылками, группировка по регионам/ОКПД2. Отправка через SMTP (Yandex 360 клиента) с вложением.
  3. Админка (FastAPI + React Admin - за 4 часа): CRUD для фильтров, просмотр логов парсеров, ручной запуск перепарса, статистика покрытия источников.
  4. Нагрузочное тестирование (k6): Симуляция опроса ЕИС каждые 30 сек + 4 ЭТП каждые 60 сек. Система держит 500 RPS на чтение БД, задержка p99 < 200 мс.
  5. Деплой на прод: Перенос на сервер клиента. Настройка systemd-юнитов для воркеров (systemd лучше K8s для 1-2 нод - проще в обслуживании сисадмину клиента). Настройка бэкапов БД (pgBackRest в S3-совместимое хранилище Selectel).

Трудности, баги и преодоление подводных камней

1. «Танцы с бубном» вокруг ЕИС API и КЭП

Проблема: Официальная документация ЕИС устарела на 2 года. Схемы XSD не соответствуют реальным ответам. Подписание запросов через pycades на Linux (Debian-based контейнер) падало с segfault из-за несовместимости версий libcpcsp и glibc. Решение:

  • Вынес signer-service в отдельный контейнер на базе ubuntu:22.04 с жестко зафиксированными версиями КриптоПро 5.0.12500.
  • Написал healthcheck, который каждые 5 мин проверяет валидность сертификата и перезапускает контейнер при зависании.
  • Закешировал подписанные XML-шаблоны запросов в Redis (TTL 1 час) - снизил нагрузку на токен в 10 раз.

2. Проблема «Дважды один и тот же лот»

Проблема: Лот появляется на ЕИС, через 10 минут дублируется на ЭТП, через час - на сайте заказчика. Система слала 3 уведомления. Решение: Ввел сущность ProcurementCluster. При сохранении нового лота ищу кластер по: purchase_number (ЕИС) ИЛИ registry_number (ЭТП) ИЛИ fuzzy-match по customer_inn + name (порог 0.92). Уведомление уходит только для мастера кластера. В карточке бота показываю вкладку «Источники» со ссылками на все дубли.

3. Изменение структуры XML протоколов посередине проекта

Проблема: В середине Спринта 2 ЕИС обновил версию схемы протоколов открытия конвертов (v2.3 -> v2.4). Парсер lxml с XPath начал падать на новых тегах extendedSignInfo. Решение: Перешел на «устойчивый парсинг»: не жду строгую структуру, а рекурсивно обхожу XML-дерево, вытягивая все теги с атрибутом name/value в плоский словарь. Схему валидирую только на входе в БД (pydantic), а не на парсинге. Добавил алерт в Grafana: «Обнаружены новые теги в XML» - срабатывает при появлении неизвестных ключей.

4. Блокировка Cloudflare на ЭТП «Закупки.МОСКВА»

Проблема: Даже с резидентными прокси и curl_cffi получал 403 на странице списка закупок. JS-челлендж не проходил. Решение: Обнаружил, что у них есть недокументированный JSON-эндпоинт для виджетов (/api/v1/procurements/list?widget=true). Он отдает те же данные, но без Cloudflare. Парсил его. Мораль: всегда ищите скрытые API в DevTools (Network -> Fetch/XHR) перед тем, как парсить HTML.


Финансовая экономика и таймлайн проекта

Параметр Значение Комментарий
Итого оплата за разработку 180 000 ₽ Фиксированный бюджет по договору ГПХ
Срок исполнения 21 календарный день 3 недели, без выходных (работал 7/7)
Чистые часы разработки ~140 часов Включая настройку инфры, CI/CD, доки
Чистая ставка в час ~1 285 ₽/ч 180 000 / 140
Расходы на инфраструктуру (дев) 4 500 ₽ VPS (Timeweb), домен, SSL, Sentry (free tier)
Расходы на прокси (Smartproxy) 3 200 ₽ Пул 50 IP на 2 недели активной разработки
Расходы на 2Captcha 800 ₽ Решение ~15 000 капч за тесты
Чистая прибыль (до налогов) 171 500 ₽ 180 000 - 8 500
Поддержка (ретейнер) 15 000 ₽/мес Мониторинг, апдейты парсеров, бэкапы, консультации
Налог (НПД 6%) -10 800 ₽ На разработку
На руки (разработка) 160 700 ₽ За 3 недели работы

Примечание: Ставка 1285 ₽/ч ниже моей рыночной (2000+ ₽/ч), но проект брал для портфолио «Госзакупки» и долгосрочного ретейнера. Риск не уложиться в дедлайн был высок, поэтому заложил 30% буфера времени - уложился ровно.


Уроки, грабли и рекомендации новичкам

  1. Не парсьте HTML, если есть API (даже кривое). ЕИС API с КЭП - боль, но оно структурированное, версионированное и легальное. HTML-верстка ЭТП ломается при каждом редизайне (раз в полгода). Инвестируйте время в реверс-инжиниринг внутренних API (GraphQL/REST) порталов - это сэкономит месяцы поддержки.
  2. Идемпотентность - закон, а не совет. Сетевые ошибки, таймауты, ретраи - норма. Ваш пайплайн должен гарантировать: один и тот же внешний ID + один и тот же хеш контента = ноль новых записей в БД. Используйте ON CONFLICT DO UPDATE в Postgres или SETNX в Redis для распределенных локов.
  3. КЭП на Linux - это отдельный микросервис. Не пытайтесь встроить pycades/pykcs11 в основной асинхронный воркер. Это нативный код, он блокирует event loop, утекает память и требует специфические либы. Изолируйте его: gRPC/HTTP внутренний API -> Контейнер с подписантом -> Ответ.
  4. Мониторинг парсеров важнее парсеров. Вы не узнаете, что парсер упал с 500 ошибкой или верстка изменилась, пока клиент не позвонит: «Почему я не видел торг №123?».
    • Обязательные метрики: parser_runs_total, parser_errors_total, items_found, items_new, lag_seconds (разница между publish_date на источнике и detected_at у вас).
    • Алерт: lag_seconds > 300 (5 мин) -> PagerDuty/Telegram.
  5. Юридическая чистота данных. В договоре обязательно пропишите: «Исполнитель не несет ответственности за полноту данных источников (ЕИС/ЭТП) и за пропуски из-за изменений API без уведомления». Клиент должен понимать: это инструмент помощника, замена юристу/закупщику не является.
  6. Начинайте с «Черного списка» заказчиков. Самый быстрый MVP - парсер, который просто проверяет ИНН заказчика по базе ФАС/судов и шлет алерт: «Внимание, у заказчика проблемы». Это дает ценность за 2 дня разработки, а не за 3 недели.

Инструмент для поиска таких заказов

Проект нашел через мониторинг заказов на биржах. Вручную отслеживать 35+ площадок (Хабр Фриланс, Кворк, FL.ru, Upwork, Телеграм-каналы) невозможно - пропустишь лучшие офферы. Использую Freelance Flow - агрегатор, который присылает релевантные заказы в Telegram за 1-3 секунды после публикации. Есть бесплатный тест на 3 дня, фильтры по стеку, бюджету и заказчику. Рекомендую настроить алерт на «Python», «Парсинг», «Госзакупки» - поток задач высокий, конкуренция адекватная.


FAQ

Нужна ли лицензия ФАС или аккредитация для разработки такого парсера?

Нет. Вы разрабатываете ПО на заказ (работу по договору подряда/ГПХ). Вы не являетесь оператором ЭТП, не проводите торги и не храните гос.тайну. Вы обрабатываете открытые данные (согласно ст. 9 44-ФЗ информация о закупках публична). Риск на стороне заказчика: он использует ваш софт для принятия решений. Защититесь в договоре пунктом об ответственности за решения, принятые на основе данных сервиса.

Как обойти защиту ЕИС/ЭТП без банов IP?

Не обходите, а работаете по правилам.

  1. ЕИС: Только официальный API + КЭП. Любые попытки скрейпить портал zakupki.gov.ru через Selenium/Playwright приведут к бану подсети и претензиям Роскомнадзора/ФАС.
  2. ЭТП: Уважайте robots.txt и Rate-Limit (обычно 1 запрос/сек с IP). Используйте пул резидентных прокси (не датацентровских!). Эмулируйте реальный TLS-фингерпринт (curl_cffi, playwright-stealth). Если есть официальное API ЭТП (у РТС, Б2Б есть) - используйте его, даже если оно платное/ограниченное. Дешевле прокси и нервов.

Как хранить и обновлять справочники ОКПД2/КТРУ/КЕКР?

Не хардкодьте. ЕИС предоставляет API для скачивания актуальных справочников в XML/JSON (/api/v1/dictionaries/...). Напишите отдельный еженедельный джоб, который качает их, парсит в ваши таблицы ref_okpd2, ref_ktru и версионирует (SCD Type 2). Фильтры пользователей должны ссылаться на ref_id, а не на строковые коды - коды меняются (например, ОКПД2 2014 -> 2017).

Что делать, если клиент требует парсить 223-ФЗ и коммерческие закупки в том же проекте?

Разделяйте контексты. 223-ФЗ - это «дикий запад»: нет единого реестра, нет единого API, каждый заказчик (госкорпорация, госучреждение) публикует на своем сайте или на своих ЭТП. Коммерческие закупки (СберЗакупки, ТЭК-Торг, Газпромбанк) - вообще закрытые контуры с авторизацией.

  • Архитектура: Общий filter-engine и notification-service, но отдельные парсеры-продюсеры для каждого правового режима.
  • Оценка: 223-ФЗ / Коммерция - это отдельный проект с бюджетом x2-x3 от 44-ФЗ. Не включайте в фикс-бюджет 44-ФЗ.

Стоит ли использовать готовые SaaS (Контур.Закупки, Закупки.РФ, ТендерПлан) вместо кастомной разработки?

Для 90% компаний - да, стоит. SaaS стоит 5-15 тыс. руб./мес, работает из коробки, имеет юристов, следящих за изменениями закона, и поддержку 24/7. Кастом нужен, если:

  1. Уникальная логика фильтрации (сложные зависимости лотов, анализ финансового состояния заказчика по Спарк/Руспрофиль, связка с 1С/ERP).
  2. Нужна интеграция во внутренние процессы (автосоздание заявки в 1С, передача в CRM, подписание ЭДО).
  3. Объем > 500 уведомлений в день - SaaS начинает тормозить или режет функционал на тарифах.
  4. Требование ИБ: данные не должны уходить на сторонние серверы (напрег для гос.корпораций).
Живая лента: перехват с 35+ бирж в реальном времени

Свежие заказы в мониторинге бота прямо сейчас

Задержка: < 2 сек
Telegram / IT Freelance•14 мин назад

Telegram-бот для приема заказов пиццерии с интеграцией iiko и эквайрингом

Pythonaiogram 3PostgreSQLiiko API
Habr Freelance•28 мин назад

Парсер цен и остатков Wildberries с обходом Cloudflare и выгрузкой в Excel

Pythoncurl_cffiPandas
FL.ru•45 мин назад

Микросервис авторизации и отправки SMS на FastAPI + Redis

FastAPIRedisDocker
⚡ 90% выгодных проектов разбирают в первые 3 минуты после публикации.Получать все заказы в Telegram мгновенно (3 дня бесплатно) →
⚡ Инструмент автоматизации поиска

Перехватывайте заказы за 1.8 секунды в Telegram

Не тратьте 4 часа в день на ручной поиск. Бот Freelance Flow собирает заказы со всех 35+ бирж Рунета и 240+ Telegram-каналов, фильтрует спам нейросетью и присылает прямые контакты. Коммерческие тарифы от 499 ₽ / 30 дней с безлимитом лидов.

Попробовать 3 дня бесплатно →