feat(scrapers): extract + download DOM.РФ flat plan images (#2440) #2441

Merged
bot-backend merged 1 commit from feat/domrf-flat-plan-image-harvest into main 2026-07-05 16:23:30 +00:00
Collaborator

Summary

  • domrf_kn_flat_plans было пусто (0 строк) — блокер для #299 (Potrace production-wiring) и #300 (CubiCasa), обе заявляли готовый корпус, которого на деле не было.
  • parse_catalog_flat (domrf_catalog.py) теперь извлекает plan_image_url: primary — __NEXT_DATA__ pageProps JSON BFS, fallback — голый <img> по class-hint/соседству с текстом «Планировка».
  • _TextCollector получает аддитивный self.images-сбор строго ДО void-tag early-return — существующий stack/buffer не тронут, регрессия по #1608 (та самая void-tag desync баг) исключена отдельным тестом.
  • download_plan_image_stub (был NotImplementedError) заменён на рабочую async-загрузку через уже существующий BrowserSession.download_binary (те же cookies/TLS/throttle), Pillow для width/height, идемпотентна.
  • Двухфазный upsert: URL пишется первым, метаданные — после скачивания. Частичный прогресс переживает падение загрузки.

Известное ограничение (не блокер, задокументировано)

  • __NEXT_DATA__ pageProps key — best-effort по паттерну, не подтверждён на живой HTML (WAF заблокировал фетч с IP агента). Fallback + absence-handling делают неверную догадку не-фатальной (вернёт None, не упадёт), но может молча давать 0 планировок пока не провалидировано на реальной странице.
  • Этот PR ships inert: scrape_one_flat/scrape_catalog_batch (domrf_catalog.py) сейчас нигде не вызываются из Celery — отдельная предпосылка issue #2440 (изначально предполагалось, что уже вызываются — прод-проверка показала, что нет). Follow-up issue заведу отдельно: Celery-wiring + catalog_url_hash propagation (elemId уже читается в domrf_kn.py:430, но не прокидывается в апсерт) + валидация pageProps-ключа на живой странице.

Test plan

  • test_domrf_catalog_parse.py + test_domrf_catalog_object.py (обязательные регрессии) — зелёные
  • 2 новых test-файла (test_domrf_flat_plan_url.py, test_flat_plan_download.py) — реалистичные фикстуры (реальный Next.js SSR JSON, реальный PNG round-trip через Pillow)
  • ruff + ruff-format чисто, psycopg ::type-скан чист, секретов в диффе нет
  • code-reviewer: APPROVE (2 minor doc-nits, не блокеры)

Refs #2440, #299, #300

## Summary - `domrf_kn_flat_plans` было пусто (0 строк) — блокер для #299 (Potrace production-wiring) и #300 (CubiCasa), обе заявляли готовый корпус, которого на деле не было. - `parse_catalog_flat` (domrf_catalog.py) теперь извлекает `plan_image_url`: primary — `__NEXT_DATA__` pageProps JSON BFS, fallback — голый `<img>` по class-hint/соседству с текстом «Планировка». - `_TextCollector` получает аддитивный `self.images`-сбор строго ДО void-tag early-return — существующий stack/buffer не тронут, регрессия по #1608 (та самая void-tag desync баг) исключена отдельным тестом. - `download_plan_image_stub` (был `NotImplementedError`) заменён на рабочую async-загрузку через уже существующий `BrowserSession.download_binary` (те же cookies/TLS/throttle), Pillow для width/height, идемпотентна. - Двухфазный upsert: URL пишется первым, метаданные — после скачивания. Частичный прогресс переживает падение загрузки. ## Известное ограничение (не блокер, задокументировано) - `__NEXT_DATA__` pageProps key — best-effort по паттерну, не подтверждён на живой HTML (WAF заблокировал фетч с IP агента). Fallback + absence-handling делают неверную догадку не-фатальной (вернёт `None`, не упадёт), но может молча давать 0 планировок пока не провалидировано на реальной странице. - **Этот PR ships inert**: `scrape_one_flat`/`scrape_catalog_batch` (domrf_catalog.py) сейчас нигде не вызываются из Celery — отдельная предпосылка issue #2440 (изначально предполагалось, что уже вызываются — прод-проверка показала, что нет). Follow-up issue заведу отдельно: Celery-wiring + `catalog_url_hash` propagation (elemId уже читается в domrf_kn.py:430, но не прокидывается в апсерт) + валидация pageProps-ключа на живой странице. ## Test plan - [x] `test_domrf_catalog_parse.py` + `test_domrf_catalog_object.py` (обязательные регрессии) — зелёные - [x] 2 новых test-файла (`test_domrf_flat_plan_url.py`, `test_flat_plan_download.py`) — реалистичные фикстуры (реальный Next.js SSR JSON, реальный PNG round-trip через Pillow) - [x] ruff + ruff-format чисто, psycopg `::type`-скан чист, секретов в диффе нет - [x] code-reviewer: ✅ APPROVE (2 minor doc-nits, не блокеры) Refs #2440, #299, #300
bot-backend added 1 commit 2026-07-05 16:08:03 +00:00
feat(scrapers): extract + download DOM.РФ flat plan images (#2440)
All checks were successful
CI / changes (pull_request) Successful in 8s
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m59s
CI / backend-tests (pull_request) Successful in 14m49s
b6e5919cf3
domrf_kn_flat_plans было пусто (0 строк) — блокер для #299 (Potrace
production-wiring) и #300 (CubiCasa). domrf_catalog.py уже фетчит SSR-
страницы каталога квартир, где лежит планировка, но _TextCollector
сознательно дропал <img> как void-tag (#1608) — картинка никогда не
извлекалась.

parse_catalog_flat теперь возвращает plan_image_url: primary-стратегия
парсит __NEXT_DATA__ pageProps JSON (Next.js SSR-паттерн), fallback —
голый <img> по class-hint/соседству с текстом "Планировка". _TextCollector
получает аддитивный self.images-сбор ДО void-tag early-return — не трогает
существующий stack/buffer, регрессия по #1608 исключена (тест
test_img_capture_preserves_text_blocks).

download_plan_image_stub (NotImplementedError) заменён на рабочую
async-загрузку через существующий BrowserSession.download_binary
(те же cookies/TLS/throttle), Pillow для width/height, идемпотентна
(skip при уже существующем файле). Двухфазный upsert: URL пишется
первым, затем метаданные после скачивания — частичный прогресс
переживает падение загрузки.

Не заводит новый Celery beat — не подключено ни в один task (домрана
предпосылка issue #2440: scrape_one_flat/scrape_catalog_batch сейчас
нигде не вызываются). Ships inert до отдельного follow-up по wiring.

Refs #2440, #299, #300
bot-backend merged commit eea9c99e73 into main 2026-07-05 16:23:30 +00:00
bot-backend deleted branch feat/domrf-flat-plan-image-harvest 2026-07-05 16:23:30 +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#2441
No description provided.