Кейс: Парсер госзакупок 44 фз на заказ
Практический кейс: парсер госзакупок 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% релевантных лотов, а на подготовку заявки оставалось критически мало времени.
Техническое задание (суть):
- Источники: ЕИС (официальный API + веб-скрапинг для резерва), 4 крупных ЭТП по 44-ФЗ.
- Фильтрация: По ОКПД2/КТРУ, регионам, НМЦК (от 5 млн ₽), ключевым словам в названии/описании, черным списку заказчиков (ФАС, суды).
- Доставка: Уведомления в Telegram-боте (карточка лота, ссылки на документацию, дедлайны) и ежедневный дайджест на почту (Excel/CSV).
- Архив: Хранение истории изменений лота (продления, протоколы, отмены) минимум 3 года.
- 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)
- Инфраструктура: Развернул стек на своем сервере для разработки. Настроил CI/CD: линтинг (ruff), типы (mypy), тесты (pytest) -> сборка образов -> деплой на стейджинг.
- ЕИС API (44-ФЗ): Это главная сложность. Официальный API требует квалифицированной ЭП (КЭП) для подписи запросов.
- Решение: Клиент выдал доступ к своему КриптоПро CSP на токене (Rutoken). Реализовал подпись через
pycades(обертка над CryptoPro) в изолированном контейнереsigner-service. Это единственный синхронный блок в системе - вынес его в отдельный воркер.
- Решение: Клиент выдал доступ к своему КриптоПро CSP на токене (Rutoken). Реализовал подпись через
- Схемы БД: Проектировал таблицы:
procurements(основная),lots,customers,documents,changes_log(история изменений),filters(правила пользователей). Индексы: GIN для JSONB полей поиска, BRIN для временных рядов. - Базовый парсер ЕИС: Реализовал метод
get_notificationsс пагинацией и фильтрами по дате изменения (lastModified). Добавил идемпотентность:UPSERTпоexternal_id+hash_content(sha256 от XML) - защита от дублей при ретраях.
Спринт 2: ЭТП, нормализация и фильтры (Дни 8-14)
- Парсеры ЭТП (РТС, Б2Б, МосОбл, Закупки.МОСКВА): У каждого своя верстка, пагинация и защита (Cloudflare, капчи).
- Стратегия: Единый базовый класс
BaseETPParserс абстрактными методамиfetch_list,parse_card,parse_docs. Конкретные парсеры - наследники. - Анти-бот: Подключил пул резидентных прокси (Smartproxy, 50 IP). Ротация на каждый запрос + случайные задержки (1-3 сек) + эмуляция заголовков браузера (
curl_cffiдля TLS-фингерпринта Chrome 120). Капчи решает 2Captcha (API key в Vault).
- Стратегия: Единый базовый класс
- Нормализация данных: Привел все источники к единой схеме
UnifiedLot(Pydantic модель). Главная боль - разные идентификаторы одной закупки на ЕИС и ЭТП. Решил через маппингpurchase_number(ЕИС) <->registry_number(ЭТП) + fuzzy-matching по названию заказчика (rapidfuzz). - Движок фильтрации: Реализовал как отдельный сервис
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)
- Telegram-бот (
aiogram 3, FSM):- Команды:
/start,/filters(инлайн-клавиатура для управления правилами),/digest(прислать Excel за вчера),/stats. - Push-уведомления: Отдельный воркер читает очередь
notificationsв Redis, батчит сообщения (не более 30 в сек, лимит TG), отправляет сdisable_notification=Trueночью. - Карточка лота: Название, заказчик, НМЦК, этап, дедлайн, кнопки «Открыть на ЕИС», «Открыть на ЭТП», «Скачать ZIP документации», «В черный список».
- Команды:
- Ежедневный дайджест: Celery-beat задача в 07:00. Сборка
openpyxlотчета с гиперссылками, группировка по регионам/ОКПД2. Отправка через SMTP (Yandex 360 клиента) с вложением. - Админка (FastAPI + React Admin - за 4 часа): CRUD для фильтров, просмотр логов парсеров, ручной запуск перепарса, статистика покрытия источников.
- Нагрузочное тестирование (k6): Симуляция опроса ЕИС каждые 30 сек + 4 ЭТП каждые 60 сек. Система держит 500 RPS на чтение БД, задержка p99 < 200 мс.
- Деплой на прод: Перенос на сервер клиента. Настройка 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% буфера времени - уложился ровно.
Уроки, грабли и рекомендации новичкам
- Не парсьте HTML, если есть API (даже кривое). ЕИС API с КЭП - боль, но оно структурированное, версионированное и легальное. HTML-верстка ЭТП ломается при каждом редизайне (раз в полгода). Инвестируйте время в реверс-инжиниринг внутренних API (GraphQL/REST) порталов - это сэкономит месяцы поддержки.
- Идемпотентность - закон, а не совет. Сетевые ошибки, таймауты, ретраи - норма. Ваш пайплайн должен гарантировать: один и тот же внешний ID + один и тот же хеш контента = ноль новых записей в БД. Используйте
ON CONFLICT DO UPDATEв Postgres илиSETNXв Redis для распределенных локов. - КЭП на Linux - это отдельный микросервис. Не пытайтесь встроить
pycades/pykcs11в основной асинхронный воркер. Это нативный код, он блокирует event loop, утекает память и требует специфические либы. Изолируйте его: gRPC/HTTP внутренний API -> Контейнер с подписантом -> Ответ. - Мониторинг парсеров важнее парсеров. Вы не узнаете, что парсер упал с 500 ошибкой или верстка изменилась, пока клиент не позвонит: «Почему я не видел торг №123?».
- Обязательные метрики:
parser_runs_total,parser_errors_total,items_found,items_new,lag_seconds(разница междуpublish_dateна источнике иdetected_atу вас). - Алерт:
lag_seconds > 300(5 мин) -> PagerDuty/Telegram.
- Обязательные метрики:
- Юридическая чистота данных. В договоре обязательно пропишите: «Исполнитель не несет ответственности за полноту данных источников (ЕИС/ЭТП) и за пропуски из-за изменений API без уведомления». Клиент должен понимать: это инструмент помощника, замена юристу/закупщику не является.
- Начинайте с «Черного списка» заказчиков. Самый быстрый MVP - парсер, который просто проверяет ИНН заказчика по базе ФАС/судов и шлет алерт: «Внимание, у заказчика проблемы». Это дает ценность за 2 дня разработки, а не за 3 недели.
Инструмент для поиска таких заказов
Проект нашел через мониторинг заказов на биржах. Вручную отслеживать 35+ площадок (Хабр Фриланс, Кворк, FL.ru, Upwork, Телеграм-каналы) невозможно - пропустишь лучшие офферы. Использую Freelance Flow - агрегатор, который присылает релевантные заказы в Telegram за 1-3 секунды после публикации. Есть бесплатный тест на 3 дня, фильтры по стеку, бюджету и заказчику. Рекомендую настроить алерт на «Python», «Парсинг», «Госзакупки» - поток задач высокий, конкуренция адекватная.
FAQ
Нужна ли лицензия ФАС или аккредитация для разработки такого парсера?
Нет. Вы разрабатываете ПО на заказ (работу по договору подряда/ГПХ). Вы не являетесь оператором ЭТП, не проводите торги и не храните гос.тайну. Вы обрабатываете открытые данные (согласно ст. 9 44-ФЗ информация о закупках публична). Риск на стороне заказчика: он использует ваш софт для принятия решений. Защититесь в договоре пунктом об ответственности за решения, принятые на основе данных сервиса.
Как обойти защиту ЕИС/ЭТП без банов IP?
Не обходите, а работаете по правилам.
- ЕИС: Только официальный API + КЭП. Любые попытки скрейпить портал zakupki.gov.ru через Selenium/Playwright приведут к бану подсети и претензиям Роскомнадзора/ФАС.
- ЭТП: Уважайте
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С/ERP).
- Нужна интеграция во внутренние процессы (автосоздание заявки в 1С, передача в CRM, подписание ЭДО).
- Объем > 500 уведомлений в день - SaaS начинает тормозить или режет функционал на тарифах.
- Требование ИБ: данные не должны уходить на сторонние серверы (напрег для гос.корпораций).
Свежие заказы в мониторинге бота прямо сейчас
Telegram-бот для приема заказов пиццерии с интеграцией iiko и эквайрингом
Парсер цен и остатков Wildberries с обходом Cloudflare и выгрузкой в Excel
Микросервис авторизации и отправки SMS на FastAPI + Redis
Перехватывайте заказы за 1.8 секунды в Telegram
Не тратьте 4 часа в день на ручной поиск. Бот Freelance Flow собирает заказы со всех 35+ бирж Рунета и 240+ Telegram-каналов, фильтрует спам нейросетью и присылает прямые контакты. Коммерческие тарифы от 499 ₽ / 30 дней с безлимитом лидов.