ДомКлик — четвёртая площадка ручного сбора Москвы #3496

Merged
lekss361 merged 1 commit from feat/msk-domclick-collector into main 2026-09-12 12:05:19 +00:00
Owner

Зачем

Авито, Циан и Яндекс по Москве уже залиты ручным сборщиком scripts/local-avito-msk/collect.py. ДомКлик оставался единственным непокрытым поставщиком, а таблица msk_raw.domclick_cards стояла пустой.

Почему локальный сборщик, а не боевой скрейпер

Разведка рекомендовала прод-путь, ссылаясь на замер «навигацией на BFF приходит только ChallengeTimeout, 9/9 и 6/6». Этот замер снят через пул мобильных прокси. Из браузера владельца повторная навигация на ту же ручку отдаёт 200 и валидный JSON: кука QRATOR решена, сессия Сбер ID жива. Проверено 12.09.2026.

Параметризация боевого скрейпера по городу нужна под регулярный сбор и остаётся отдельной задачей. Здесь — разовая заливка тем же инструментом, что и три предыдущие площадки.

Что внутри

Параметр Значение Как получено
GUID Москвы 1d1463ae-…b553 снят с куки после сброса региона
aids не нужен без него счётчик сходится с заголовком выдачи
Размер страницы 20 API
Потолок страниц 100 offset=1980 ок, 2000 → HTTP 400
Вторичка Москвы 23 690 pagination.total
Коридоров в плане 24, максимум 1425 потолок 2000 не задет

Счётчик берётся из pagination.total выдачи, а не из отдельной count-ручки: та считает офферы вместе с дублями (25 898 против 7 651 на двушках), и разница пагинацией недостижима.

Маппинг переиспользует DomClickScraper._map_item из кита. Гео-гард собственный, по bbox Москвы с ТиНАО: _is_geo_ok кита сверяет offerRegionName с «Екатеринбургом», а в московской выдаче встречаются «x. Говорово», «пос. Птичное», «x. Ильичевка» — гард по названию региона молча резал бы легитимные лоты Новой Москвы.

Тихий дефект, который чинит этот PR

page.content() отдаёт сериализованный документ, а не тело ответа: Chrome экранирует в текстовом узле &, <, > и неразрывный пробел. Синтаксис JSON не страдает, поэтому разбор проходит зелёным, а в адресе и описании оседают &amp; и &nbsp; — 29 штук на одной странице из двадцати карточек.

Кавычки в список замен не входят намеренно: сериализатор экранирует их только в атрибутах, а в текстовом узле кавычка остаётся собой, и разворот &quot; дал бы невалидную escape-последовательность внутри строки JSON.

Test plan

  • Живой запрос через Loader: 20 карточек, geo_filtered 0, parse_failures 0
  • Первая тысяча строк на проде: 0 без цены, 0 без координат, 0 без площади, 0 с остатками экранирования, координаты внутри bbox
  • Нечисловая координата отбрасывает одну карточку, прогон не падает
  • pre-commit run --files — чисто
  • Ревью — APPROVE, критичных замечаний нет
  • Полный сбор 23 690 карточек (идёт, ~1.5 ч)

Разведка записана в волт: research/Domclick_Msk_Bff_Recon_Sep12.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NQb6WeJtagZwZnUsSjDizs

## Зачем Авито, Циан и Яндекс по Москве уже залиты ручным сборщиком `scripts/local-avito-msk/collect.py`. ДомКлик оставался единственным непокрытым поставщиком, а таблица `msk_raw.domclick_cards` стояла пустой. ## Почему локальный сборщик, а не боевой скрейпер Разведка рекомендовала прод-путь, ссылаясь на замер «навигацией на BFF приходит только ChallengeTimeout, 9/9 и 6/6». Этот замер снят через пул мобильных прокси. Из браузера владельца повторная навигация на ту же ручку отдаёт 200 и валидный JSON: кука QRATOR решена, сессия Сбер ID жива. Проверено 12.09.2026. Параметризация боевого скрейпера по городу нужна под **регулярный** сбор и остаётся отдельной задачей. Здесь — разовая заливка тем же инструментом, что и три предыдущие площадки. ## Что внутри | Параметр | Значение | Как получено | |---|---|---| | GUID Москвы | `1d1463ae-…b553` | снят с куки после сброса региона | | `aids` | не нужен | без него счётчик сходится с заголовком выдачи | | Размер страницы | 20 | API | | Потолок страниц | 100 | `offset=1980` ок, `2000` → HTTP 400 | | Вторичка Москвы | 23 690 | `pagination.total` | | Коридоров в плане | 24, максимум 1425 | потолок 2000 не задет | Счётчик берётся из `pagination.total` выдачи, а не из отдельной count-ручки: та считает офферы вместе с дублями (25 898 против 7 651 на двушках), и разница пагинацией недостижима. Маппинг переиспользует `DomClickScraper._map_item` из кита. Гео-гард собственный, по bbox Москвы с ТиНАО: `_is_geo_ok` кита сверяет `offerRegionName` с «Екатеринбургом», а в московской выдаче встречаются «x. Говорово», «пос. Птичное», «x. Ильичевка» — гард по названию региона молча резал бы легитимные лоты Новой Москвы. ## Тихий дефект, который чинит этот PR `page.content()` отдаёт сериализованный документ, а не тело ответа: Chrome экранирует в текстовом узле `&`, `<`, `>` и неразрывный пробел. Синтаксис JSON не страдает, поэтому разбор проходит зелёным, а в адресе и описании оседают `&amp;` и `&nbsp;` — 29 штук на одной странице из двадцати карточек. Кавычки в список замен не входят намеренно: сериализатор экранирует их только в атрибутах, а в текстовом узле кавычка остаётся собой, и разворот `&quot;` дал бы невалидную escape-последовательность внутри строки JSON. ## Test plan - [x] Живой запрос через `Loader`: 20 карточек, `geo_filtered` 0, `parse_failures` 0 - [x] Первая тысяча строк на проде: 0 без цены, 0 без координат, 0 без площади, 0 с остатками экранирования, координаты внутри bbox - [x] Нечисловая координата отбрасывает одну карточку, прогон не падает - [x] `pre-commit run --files` — чисто - [x] Ревью — APPROVE, критичных замечаний нет - [ ] Полный сбор 23 690 карточек (идёт, ~1.5 ч) Разведка записана в волт: `research/Domclick_Msk_Bff_Recon_Sep12.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NQb6WeJtagZwZnUsSjDizs
lekss361 added 1 commit 2026-09-12 12:04:17 +00:00
feat(msk-collector): адаптер DomClick — четвёртая площадка ручного сбора Москвы
All checks were successful
CI Trade-In / changes (pull_request) Successful in 16s
CI / changes (pull_request) Successful in 16s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / browser-tests (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
b4ca450f18
Авито, Циан и Яндекс по Москве уже залиты этим скриптом; ДомКлик был
единственным непокрытым поставщиком, а таблица msk_raw.domclick_cards стояла
пустой. Адаптер закрывает пробел.

## Почему локальный сборщик, а не параметризация боевого скрейпера

Разведка настаивала на прод-пути, ссылаясь на замер «навигацией на BFF
приходит только ChallengeTimeout, 9/9 и 6/6». Замер снят через пул мобильных
прокси и к браузеру владельца не относится: оттуда повторная навигация на ту
же ручку отдаёт 200 и валидный JSON — кука QRATOR в контексте уже решена, а
сессия Сбер ID жива (auth/me → 200). Проверено 12.09.2026.

Параметризация боевого скрейпера по городу остаётся отдельной задачей под
РЕГУЛЯРНЫЙ сбор. Здесь нужна разовая заливка тем же инструментом, которым
сделаны три предыдущие площадки.

## Устройство

Регион задаётся GUID-ом Москвы в параметре address; aids, захардкоженный для
ЕКБ, Москве не нужен — без него счётчик сходится с числом в заголовке выдачи.

Бисекция — по цене, без комнатных корзин. Потолок пагинации замерен: offset
1980 отдаёт 20 карточек, 2000 даёт HTTP 400. Отсюда page_size 20, max_pages
100, hard_cap 2000. Полная вторичка Москвы — 23 690 по pagination.total, и
план из 24 коридоров укладывается в потолок с запасом (максимум 1425).

Счётчик берётся из pagination.total самой выдачи, а НЕ из отдельной
count-ручки: та считает офферы вместе с дублями (25 898 против 7 651 на
двушках) и для бисекции непригодна — разница пагинацией недостижима.

Маппинг переиспользует DomClickScraper._map_item из кита. Гео-гард —
собственный, по bbox Москвы с ТиНАО: _is_geo_ok кита сверяет
offerRegionName с «Екатеринбургом», а в московской выдаче встречаются
«x. Говорово», «пос. Птичное», «x. Ильичевка» — гард по названию региона
молча резал бы легитимные лоты Новой Москвы.

## Экранирование в <pre>

page.content() отдаёт сериализованный документ, а не тело ответа, и Chrome
экранирует в текстовом узле амперсанд, угловые скобки и неразрывный пробел.
Синтаксис JSON от этого не страдает, поэтому дефект тихий: разбор проходит, а
в адресе и описании оседают &amp; и &nbsp; — 29 штук на одной странице из
двадцати карточек. Добавлен _unescape_pre_entities.

Кавычки в список замен не входят намеренно: сериализатор экранирует их только
в значениях атрибутов, а в текстовом узле кавычка остаётся собой. Разворот
&quot; превратил бы экранированную кавычку внутри строки JSON в невалидную
последовательность и уронил бы разбор страницы целиком.

## Проверки

Живой запрос через Loader: 20 карточек, geo_filtered 0, parse_failures 0.
Первая тысяча строк на проде — 0 без цены, 0 без координат, 0 без площади, 0
с остатками экранирования, координаты внутри bbox.

Нечисловая координата отбрасывает одну карточку, а не роняет прогон: сбор
идёт часами без присмотра, и падение на середине коридора стоило бы дороже
потерянного лота.

Claude-Session: https://claude.ai/code/session_01NQb6WeJtagZwZnUsSjDizs
lekss361 merged commit 628f59dcc0 into main 2026-09-12 12:05:19 +00:00
lekss361 deleted branch feat/msk-domclick-collector 2026-09-12 12:05:19 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3496
No description provided.