diff --git a/docs/superpowers/specs/2026-07-05-self-render-2gis-yandex-channel-design.md b/docs/superpowers/specs/2026-07-05-self-render-2gis-yandex-channel-design.md new file mode 100644 index 00000000..6cf91041 --- /dev/null +++ b/docs/superpowers/specs/2026-07-05-self-render-2gis-yandex-channel-design.md @@ -0,0 +1,135 @@ +# Свой канал 2ГИС+Яндекс на выделенной виртуалке — дизайн + +**Дата:** 2026-07-05 · **Ветка:** `worktree-avtopodbor` (НЕ прод) · **Статус:** дизайн утверждён владельцем, готов к плану. + +## 1. Цель и мотивация + +Сбор конкурентов (шаг 1) и изучение (шаг 2) сейчас тянут данные 2ГИС через платный внешний сервис **xf4.ru**. Замер 05.07.2026 на реальных данных владельца (Красноярск, займы под залог авто): + +| | xf4 (сейчас) | наш рендер (тест) | +|---|---|---| +| Полный шаг 1 | **25 мин** | **9 мин** | +| 2ГИС-листинг | 264с / 127 фирм | 53с / 51 фирма | +| Вскрытие карточек 2ГИС | 967с (64% всего времени) | часть 340с | +| Деньги за прогон | ~50–75 ₽ (при потоке — тысячи ₽/день) | ~0 (только ИИ) | + +**Тормоз — сам сервис xf4:** лимит ~3–4 запроса/ключ (429 выше), рендер 20–60с/страница. Ключами не уравнять: 2ГИС-листинг (264с, ключами не ускоряется) один больше всей Яндекс-части (194с). + +**Проверено вживую:** +- наш локальный Playwright с маскировкой (скрытый `webdriver` + свежий Chrome UA + флаг `AutomationControlled` + проскок заглушки «обновите браузер») **пробивает 2ГИС** — 600–680 КБ реальных данных, живые телефоны; +- залп 28 карточек по 5 параллельно с одного IP — **100% данные, IP выдержал** (баг был в тесте, не в IP); +- голый Playwright 2ГИС **блокирует** (капча/заглушка) — маскировка обязательна. + +**Цель:** заменить xf4 на свой рендер на выделенной виртуалке — **быстро и качественно**, с ротацией прокси, за флагом, с откатом на xf4. + +## 2. Планка приёмки (definition of done для включения флага) + +На тест-клиенте новый канал обязан: +1. Находить фирм **не меньше**, чем xf4 (эталон ~127 для тестового запроса), а не 51. +2. **Ноль мусора** — только релевантные фирмы (никаких «Бургер Кинг», парковок, табачных из соседних зданий). +3. Телефоны/сайты/названия — **корректные, собственные данные 2ГИС**. +4. Ручная сверка **фирма-в-фирму с живым 2ГИС** по нескольким фирмам — совпадает. + +Флаг включаем **только после** этой сверки владельцем. + +## 3. Границы — что НЕ меняется (жёстко) + +**UI, кнопки, функции, экраны автоподбора НЕ трогаются.** Правится только слой добычи сырых данных. + +Остаются как есть: экраны «Конкурентное поле» / «Предложения» / «Источники в работе»; кнопки «Собрать конкурентов», «Дайте источники», «В источники →», «Изменить», «Удалить», «Добавить вручную», окно слияния «останется», «История слияний», «Вернуть карточку», перенос выбранных, поиск дублей; логика списания денег, дедуп, склейка, похожесть-%, метки ✓/🎭, шаги 1/2. Клиент работает через тот же портал Лидерра, разницы в интерфейсе не видит. + +## 4. Архитектура (вариант A — весь автоподбор на виртуалке) + +``` +Клиент → Портал Лидерра → задача в очередь Redis (autopodbor-find / autopodbor-study) + ↓ + ВИРТУАЛКА = второй рабочий автоподбора + ├─ queue:work --queue=autopodbor-find / autopodbor-study + ├─ Рисовалка: постоянный браузер + маскировка + прокси-ротация + ├─ 2ГИС: перехват встроенного JSON → чистые фирмы + ├─ Яндекс: та же маскировка + прокси (разбор прежний) + └─ пишет результат в ту же базу (Managed PG) + ↓ + Клиент видит результат в портале +``` + +- Виртуалка запускает воркеры автоподбора и делает всю browser-работу; браузерный шторм физически не на боевом портале. +- Разнесённые очереди `autopodbor-find` / `autopodbor-study` (уже в коде) позволяют при желании развести шаги по разным воркерам/виртуалкам. +- Всё за флагом `self_render`; xf4-путь остаётся целым запасным. + +## 5. Компоненты + +### 5.1. Рисовалка (render engine) +- **Один браузер держим открытым**; на каждую страницу — отдельный лёгкий **контекст** (свои куки + свой прокси), отрисовали → закрыли контекст, браузер живёт. Убирает трату ~1–2с на запуск браузера × сотни карточек. +- **Маскировка** (проверена): скрытый `navigator.webdriver`, свежий Chrome UA, `--disable-blink-features=AutomationControlled`, `ru-RU`, реалистичный viewport; проскок заглушки «обновите браузер» (клик «перейти в 2ГИС»). +- **Прокси на каждый контекст** — из пула, round-robin. +- **Параллельность** — настройка под память виртуалки (~300–400 МБ на контекст). Слабая машина — 4, мощная — 16–30. +- **Ошибки:** тайм-аут/редкий блок → **повтор через другой прокси** (1–2 раза) → не вышло → пропуск карточки (как сейчас, поток не падает). Падение браузера → перезапуск. + +### 5.2. 2ГИС через JSON (чистые данные) +- При загрузке страницы 2ГИС сам подтягивает **встроенные пакеты данных (JSON)** — его собственный структурированный список фирм (название, адрес, телефоны, сайт). **Перехватываем эти ответы** (Playwright видит сетевые ответы страницы) и читаем готовые данные, а не рисованный HTML. +- Новый разборщик читает JSON → чистый список фирм. Старый HTML-разборщик 2ГИС **остаётся для xf4-пути**, не трогается. +- **Первый шаг стройки:** поймать реальный JSON живого 2ГИС (листинг + карточка), зафиксировать формат как тест-фикстуру, на нём написать разборщик по TDD. + +### 5.3. Яндекс +- Уже рисуется своим браузером и работает без мусора → **разбор не переделываем**. Добавляем только **прокси-ротацию** и перенос на виртуалку. + +### 5.4. Прокси-слой +- Пул адресов в настройке (`.env`/config), round-robin per-context, одинаково для 2ГИС и Яндекса. **Провайдер-агностично** — любой сервис резидентных/мобильных RU-прокси (кандидаты: ProxyGRAM, Proxy.Market, Proxys.io, MangoProxy). Ключи только в `.env`, в гит не идут. + +### 5.5. Флаг и откат +- Флаг `autopodbor.self_render` (env, по умолчанию ВЫКЛ). ВКЛ → 2ГИС+Яндекс через свой рендер+прокси. ВЫКЛ → текущий путь через xf4. Мгновенный откат тумблером. + +### 5.6. Выкат на виртуалку +- Виртуалка = второй app-узел: копия приложения, доступ к Redis и Managed PG (внутренняя сеть облака), node+Playwright+Chromium, пул прокси в `.env`, воркеры `autopodbor-find`/`autopodbor-study`. Параллельность контекстов — по памяти виртуалки. Выкат через `prod-deploy-validator`. + +## 6. Обработка ошибок + +| Ситуация | Поведение | +|---|---| +| Тайм-аут/пустая страница | повтор через другой прокси ×1–2 → пропуск карточки | +| Заглушка «обновите браузер» | автоклик «перейти в 2ГИС» | +| Блок по IP | смена прокси (ротация) | +| Падение браузера | перезапуск постоянного браузера | +| Прокси недоступен | следующий из пула; пул пуст → лог + деградация (как xfetch без ключа) | +| 2ГИС сменил формат JSON | разбор пуст → лог; фикстуры-тесты ловят регресс | + +## 7. Тестирование (TDD) + +- **Разборщик 2ГИС** — юнит-тесты на **реальных сохранённых JSON-фикстурах** (листинг + карточка): фирма-в-фирму, телефоны, отсев мусора. По TDD. +- **Рисовалка** — интеграция: отдаёт данные с тест-прокси; маскировка/проскок работают. +- **Прокси-ротация** — юнит: раскидывает контексты по пулу по кругу. +- **Приёмка** — полный прогон на деве + ручная сверка «фирма-в-фирму» с живым 2ГИС (владелец). +- **Регресс:** все **434 текущих теста автоподбора зелёные** (xf4-путь не тронут). + +## 8. Фазы стройки (каждая проверяема) + +1. **Чистый разбор 2ГИС.** Поймать реальный JSON → разборщик по TDD. *Без виртуалки/прокси.* +2. **Рисовалка.** Постоянный браузер + маскировка + поддержка прокси. *Локально.* +3. **Сборка за флагом + приёмка.** Полный прогон на деве, сверка «фирма-в-фирму». *Ещё на деве, флаг ВЫКЛ по умолчанию.* +4. **Выкат на виртуалку.** Провижн виртуалки + пул прокси + `prod-deploy-validator` GO/NO-GO. + +## 9. Требования к железу + +- Ограничитель — **память** (~300–400 МБ на параллельный контекст) + процессор (~0,5–1 ядро/контекст). +- **Формула:** параллельных контекстов ≈ свободная память ÷ 350 МБ (и не больше ~1,5× ядер). + +| Свободно памяти | Параллельно | Один прогон шаг 1 | +|---|---|---| +| 2,5 ГБ (dev сейчас) | 4 | ~9 мин | +| 6 ГБ | ~16 | ~3–4 мин | +| 12 ГБ | ~30 | ~2 мин | + +- **Рекомендация для боевого:** выделенная виртуалка **8–16 ГБ свободной памяти + 4–8 ядер** под автоподбор → 16–30 контекстов → прогон 2–4 мин или 2–3 клиента разом. +- Поток (владелец): 5–6 клиентов/день, шаг 1 редкий (~1,2 прогона/клиент), шаг 2 частый но лёгкий (~70 конкурентов на всех), не одновременно — под это 8 ГБ виртуалки с запасом. + +## 10. Открытые вопросы (решаются на этапе внедрения, не блокируют стройку кода) + +- Конкретный провайдер прокси и бюджет (закупка; система агностична). +- Точный размер виртуалки (по факту нагрузки; старт с 8 ГБ). +- Провижн виртуалки (образ/деплой) — ops-задача фазы 4. + +## 11. Артефакты, уже готовые в ветке + +- Рабочий рецепт маскировки — `app/scripts/render-2gis-stealth.cjs` (образец, не подключён). +- Мульти-ключ xfetch (коммит `bdecd08d`) и раздельные очереди find/study (`43e29823`) — совместимы, остаются как запасной путь и основа для разнесения по виртуалкам.