Вихідна ситуація: у відділі закупівель щоденно відстежуються нові тендери з шести електронних торгових майданчиків (ETP). Зараз це робиться вручну в браузері з фільтрами, через що частина релевантних закупівель пропускається, а дані різних ETP не наводяться до єдиного вигляду. Технічне середовище та обмеження: Linux-сервер (Ubuntu 22.04), доступ до SSH. Можна використовувати Python 3.11, Playwright або Selenium, requests/httpx, PostgreSQL 14 (БД вже є). Docker можна, але не обов'язковий. Не можна використовувати платні антикапчі-сервіси та не можна зберігати облікові дані від особистих кабінетів у відкритому вигляді. Доступ до ETP лише як гість, без авторизації. У частині сайтів контент підвантажується JavaScript, є антибот-захисту та ліміти на частоту запитів. Предметна галузь: тендери/закупівлі. Потрібно збирати картки закупівель та лоти, нормалізувати категорії та суми, зберігати історію змін. Що має вийти: автономний парсер, який за розкладом збирає дані з 6 заданих ETP (список доменів та приклади посилань на видачу надам), наводить їх до загальної схеми та складає в PostgreSQL, а також надсилає повідомлення до Telegram при появі нових релевантних закупівель. Етапи та ключові вимоги: 1) Аналіз джерел - для кожної ETP визначити спосіб завантаження (XHR/GraphQL/HTML/JS-render), точки входу видачі та картки, набір полів, обмеження швидкості. 2) Реалізація збирача - окремі модулі-адаптери за кожним джерелом з єдиним інтерфейсом. Повинні підтримуватися повтори, backoff, таймаути, обмеження паралелізму, ротація User-Agent, робота через проксі (проксі надам) та стійкість до часткових збоїв. 3) Нормалізація даних - єдина модель у БД: закупівля, лот, замовник, регіон, дати (публікація/закінчення), НМЦ/валюта, статус, посилання, майданчик, текстовий опис, CPV/ОКПД2 (якщо є), а також обчислюване поле релевантності за ключовими словами та виключаючи. Суми призвести до числа, дати до UTC, видалити дубль-оголошення між ETP за набором евристик (ІПН замовника + сума + дати + схожість назви). 4) Зберігання історії - при зміні статусу або дати закінчення повинен фіксуватися новий запис у таблиці історії з timestamp та зміненими полями. 5) Повідомлення - Telegram-бот надсилає повідомлення про нові релевантні закупівлі, не більше 1 повідомлення на закупівлю, з короткими полями та посиланням. Також команда /health повертає статистику останнього прогону (скільки зібрано за джерелами, скільки помилок, час). 6) Експлуатація - запуск по cron або systemd timer, логування у файл, зрозумілі повідомлення про помилки щодо кожного джерела. Конкретний результат: репозиторій з вихідним кодом, конфігурацією (env/ini/yaml), SQL міграціями схеми, інструкцією запуску на Ubuntu, а також прикладом конфігурації фільтрів релевантності. Вимірні критерії приймання: - На тестовому прогоні за 24 години парсер збирає не менше 95% оголошень з видачі по кожному джерелу в межах перших 3 сторінок (звіряння за ручним підрахунком на вибірці з 30 оголошень на джерело). - Дублі в БД не перевищують 2% від загальної кількості записів за прогін (перевірка за унікальним ключем та евристиками дедуплікації). - Повторний запуск на тих же фільтрах не створює нових записів закупівель, якщо на джерелах немає змін, а історія поповнюється лише за реальних змін полів. - Час повного прогону з усіх джерел не більше 45 хвилин на сервері 2 vCPU/4 GB RAM при обмеженні до 3 паралельних завдань. - Логи містять роздільну статистику за джерелами та коди причин помилок (таймаут, блок, парсинг, структура змінилася). Додатково: список ETP, приклади URL, ключові слова та доступ до тестової БД надам після погодження підходу та структури даних.