Черновик регулярного сбора по Москве и области: сетка якорей, ID площадок, гео-профили ДомКлика #3533

Merged
lekss361 merged 6 commits from feat/msk-sweep-draft into main 2026-09-15 17:51:32 +00:00

6 commits

Author SHA1 Message Date
bot-backend
450940cafb fix(scheduler): опечатка в city расписания падает, а не собирает Екатеринбург
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 14s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m53s
CI ветки краснел на test_job_avito_city_sweep_unknown_city_falls_back_to_none:
тест закреплял прежний контракт «неизвестный слаг → anchors=None», а реестр теперь
бросает ValueError вместо молчаливого отката на ЕКБ.

Правильное поведение — падать. anchors=None уводит свип на EKB_ANCHORS, то есть
опечатка в default_params.city давала полноценный прогон по Екатеринбургу,
проштампованный именем чужого города. Тик планировщика ValueError ловит
(logger.exception в _run), цикл не умирает — крашится только этот прогон.

Отсутствие ключа city по-прежнему даёт anchors=None и citywide-свип ЕКБ: это
контракт старых расписаний, он не тронут. Комментарий на call-site приведён в
соответствие — он всё ещё описывал возврат None.
2026-09-15 20:45:47 +03:00
bot-backend
4646c3b79e feat(scraper): якоря области — 22 города + 22 кластера лот-массы вместо TODO
Some checks failed
CI Trade-In / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 15s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 5m9s
Черновик оставлял TODO: 10 крупных городов области без координат («выдуманные
координаты хуже отсутствующих») и без замера, сколько лотов у какого города.
Оба пробела закрыты измерением, ни одна координата не выдумана.

Слой 1 — города-спутники, 22 вместо 12. Недостающие 10 (Домодедово, Раменское,
Щёлково, Сергиев Посад, Ногинск, Электросталь, Серпухов, Коломна, Орехово-Зуево,
Наро-Фоминск) взяты запросом к Nominatim /search 15.09.2026, все вернулись как
place=city/town.

Слой 2 — кластеры лот-массы, 22 точки: центроиды активных объявлений области в
ячейках 0.05°, добранные жадным покрытием поверх слоя 1; подпись — ближайший НП
по обратному геокодированию, справочная.

Второй слой нужен потому, что городов мало. Замер на проде по 70 996 активным
объявлениям region_code=50: 22 городских якоря радиусом 10 км накрывают 73.8%
лот-массы, вместе со вторым слоем — 95.2%.

Порядок в обоих слоях — по убыванию измеренной лот-массы; в комментарии у каждой
строки лоты в 10 км (слой 1) или прирост покрытия (слой 2). Это и есть тот замер,
которого раньше не было — TODO о ранжировании снят.

MOSCOW_OBLAST_ANCHOR_RADIUS_M = 10000 зафиксирован константой: 95.2% относятся
именно к 10 км, меньший radius_m в расписании даст дыры.

Оговорка записана в коде: лот-масса взята из уже собранного корпуса (разовая
загрузка 10-12.09.2026), поэтому 95.2% — оценка по тому, что видно, а не
доказательство полноты. Первый регулярный прогон обязан пересчитать покрытие.
2026-09-15 20:17:45 +03:00
bot-backend
02d4691841 fix(domclick): страница прогрева Москвы и области — апексный домен
Some checks failed
CI Trade-In / changes (pull_request) Successful in 15s
CI / changes (pull_request) Successful in 20s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Failing after 5m25s
Черновик профилей 77/50 угадывал субдомен по схеме `<город>.domclick.ru`.
Замер 15.09.2026 показал, что у Москвы своего субдомена нет:

  msk.domclick.ru                -> 301 https://domclick.ru/...
  moskovskaya-oblast.domclick.ru -> 301 https://domclick.ru/...
  domclick.ru                    -> 401 (QRATOR-челлендж — живая выдача)
  ekaterinburg.domclick.ru       -> 401 (контроль)

Прогрев по 301 ушёл бы на другой URL и с большой вероятностью потерял бы
куку QRATOR, а подзапрос к BFF без неё отдаёт ChallengeTimeout. Профиль 66
не тронут.
2026-09-15 20:06:45 +03:00
bot-backend
eff974890e feat(scraper-kit): ценовой планировщик коридоров + счётчики усечения
Потолок выдачи площадки (Avito ~1500 / Cian 1512 / Yandex 500 / DomClick 2000)
по Свердловской области не мешал — вторичка влезает в сид-брекеты. По Москве и
области лотов десятки тысяч, и прогон молча забирал верхушку выдачи, рапортуя
успех. Движок бисекции в ките уже был, но усечение он нигде не фиксировал.

pricing/planner.py — синхронный ЧИСТЫЙ планировщик plan_price_corridors():
на вход count_of(lo, hi), на выход коридоры + PlanStats. Алгоритм перенесён из
обкатанного живьём scripts/local-avito-msk/collect.py: геометрическая середина
sqrt(lo*hi) (цены логнормальны — арифметическая отрезает пустую верхнюю
половину), поиск верхней границы удвоением от 8 млн, предохранители MAX_DEPTH=12
и MIN_WIDTH_RATIO=1.05. Неделимый переполненный коридор помечается truncated, а
его недобор попадает в missed_estimate.

Добавлен третий предохранитель, которого в образце нет: атомарный коридор
(hi-lo<=1) не имеет строгой внутренней точки, geometric_mid вернул бы саму
границу и половина совпала бы с родителем — рекурсия по себе, от которой
collect.py спасает только MAX_DEPTH. Покрыто тестом.

pricing/bisection.py — BisectionStats: probes/leaves/truncated_leaves/
missed_estimate/unknown_leaves/skipped_probe_fail/degraded. Параметр stats
опционален и по умолчанию None, поэтому дерево рекурсии не меняется ни на
ветку — golden-parity всех четырёх провайдеров на месте (21 существующий тест
бисекции зелёный).

Регион 66 и НЕИЗВЕСТНЫЙ регион — бисекция выключена: там она не нужна (всё
влезает в сид-брекеты), а лишний probe — лишний запрос под антибот. None
трактуется как 66, а не как «включить»: провайдеры фоллбэчат на ЕКБ-дефолт, и
«безопаснее включить» означало бы молча поменять боевое поведение прогонов,
которые просто не прокинули параметр. Выключенный режим всё равно делает
корневой probe и репортит усечение — молчания нет и там.
2026-09-15 20:00:50 +03:00
bot-backend
a4ba03ce8e feat(scraper): снять привязку avito/cian/yandex к Екатеринбургу
Черновик московской развёртки: реестры городов перестают быть свердловскими.

- cian: хост уехал из классового base_url в CityLocation.cian_host — поддомен
  региональный (ekb.cian.ru — вся Свердловская обл., www.cian.ru — Москва и МО)
  и обязан ехать вместе с region=, иначе они разъедутся молча. ЕКБ идёт по
  None-пути: URL байт в байт прежний.
- реестры: moskva (cian region=1 / rgid 587795), moskovskaya_oblast (4593 /
  587654) и объединённый moskva_i_mo (-1 / 741964) — значения замерены живьём
  10-12.09.2026. У avito слаг один на оба региона (moskva_i_mo), деление по
  регионам делает импорт по полигонам.
- якоря Москвы: сетка 5x5 = 25 узлов по ядру 55.55..55.95 / 37.30..37.90, шаг
  0.10x0.15 градуса — полудиагональ ячейки 7.27 км против радиуса запроса 8 км,
  дыр в покрытии нет. Центр ядра стоит первым: его берёт гео-гард штампа.
  У области — по якорю на город-спутник, координаты из реестра репозитория.
- радиус доверия штампа Москвы 30 км (дальний угол ядра 29.1 км) — дефолтные
  15 км срезали бы city у легитимных лотов за МКАД.
- avito_serp_ekb_only перестал быть глобальным «только Екатеринбург»: на слаге
  РЕГИОНА per-card фильтр выключается (в ссылках карточек /moskva/, /himki/ —
  слаг moskva_i_mo там не встречается, фильтр выбросил бы всю выдачу).
- неизвестный слаг теперь ПАДАЕТ во всех трёх резолверах вместо тихого отката
  на Екатеринбург; у многогородних скоупов city=NULL вместо ложного штампа.

Строк расписаний и ценовой бисекции здесь нет, живых прогонов не было.
2026-09-15 19:49:57 +03:00
bot-backend
660aced334 feat(domclick): гео-скоуп свипа — параметр региона вместо зашитого ЕКБ
Черновик регионального свипа: GUID, aids, страница прогрева QRATOR, bbox гарда
и имя города собраны в DomClickGeoProfile и реестр DOMCLICK_GEO_PROFILES
(66/77/50). Раньше это были модульные константы _EKB_*, поэтому строка
расписания с region_code 77/50 собрала бы Екатеринбург и записала его под чужим
регионом молча — pipeline отбивал такой прогон ValueError'ом; теперь вместо
гарда резолвится профиль, а падение остаётся только на коде без профиля.

Значения 66 сохранены байт в байт (тест сверяет offers- и count-URL с
литералами, снятыми до правки). У Москвы и области aids не добавляется: без
него счётчик совпадает с сайтом (замер 12.09.2026). Имя региона сверяется
только там, где задано: offerRegionName вне ЕКБ ненадёжен — офферы Новой
Москвы приходят как «г. Говорово», поэтому для 77/50 гард только по bbox.
bbox 77/50 продублированы из app.services.regions REGIONS[code].bbox_region —
кит не имеет права импортировать app.*.

Не проверено живьём: страница прогрева для 77/50 (msk.domclick.ru) — единственное
поле профилей, взятое по схеме субдомена, а не замером.
2026-09-15 19:35:28 +03:00