fix(scrapers): yandex area из embedded-state JSON (Refs #775) #783

Merged
bot-reviewer merged 1 commit from fix/775-embedded-state-parse into main 2026-05-30 16:38:13 +00:00
Collaborator

Часть A (#24) — Yandex area NULL у 82%

yandex_realty.py парсил площадь из RAW-HTML regex, пустого до JS-гидрации → 82% NULL area. _parse_html теперь извлекает {offer_id: area_m2} из <script id="initial_state_script"> (или application/json) раз на страницу и использует preferentially в _card_to_lot; DOM-regex остаётся fallback'ом. Поддержаны 4 offer-path (offers/pageData.offers/listing.offers/serpData.items) + 5 area-полей (totalArea/area/spaceTotal/spaceAll/squareTotal, incl nested {value}). 6 новых тестов.

Часть B (#23) — cian buildingCadastralNumber

⚠️ Уже реализовано — премиса аудита устарела: cian.py:291 читает offer.buildingCadastralNumber, :456 пишет в ScrapedLot.building_cadastral_number, поле base.py:109 существует, тест test_cian_serp_cadastral_numbers зелёный. cian.py не трогался, изменений не нужно.

Тесты

ruff clean; pytest -k "yandex or cian" → 365 pass. NB: 1 pre-existing unrelated fail test_cian_valuation.py::test_cache_hit_returns_cached (KeyError: low_price в mock-фикстуре) — НЕ из этого PR.

Refs #775

## Часть A (#24) — Yandex area NULL у 82% `yandex_realty.py` парсил площадь из RAW-HTML regex, пустого до JS-гидрации → 82% NULL area. `_parse_html` теперь извлекает `{offer_id: area_m2}` из `<script id="initial_state_script">` (или `application/json`) раз на страницу и использует preferentially в `_card_to_lot`; DOM-regex остаётся fallback'ом. Поддержаны 4 offer-path (`offers`/`pageData.offers`/`listing.offers`/`serpData.items`) + 5 area-полей (`totalArea`/`area`/`spaceTotal`/`spaceAll`/`squareTotal`, incl nested `{value}`). 6 новых тестов. ## Часть B (#23) — cian buildingCadastralNumber ⚠️ **Уже реализовано** — премиса аудита устарела: `cian.py:291` читает `offer.buildingCadastralNumber`, `:456` пишет в `ScrapedLot.building_cadastral_number`, поле `base.py:109` существует, тест `test_cian_serp_cadastral_numbers` зелёный. **cian.py не трогался**, изменений не нужно. ## Тесты `ruff` clean; `pytest -k "yandex or cian"` → 365 pass. NB: 1 pre-existing unrelated fail `test_cian_valuation.py::test_cache_hit_returns_cached` (`KeyError: low_price` в mock-фикстуре) — НЕ из этого PR. Refs #775
bot-backend added 1 commit 2026-05-30 16:35:53 +00:00
Часть A (#24): yandex_realty.py парсил площадь из RAW-HTML (regex), пустого до
JS-гидрации → 82% NULL area. _parse_html теперь извлекает {offer_id: area_m2}
из <script id=initial_state_script> (или application/json) раз на страницу и
использует preferentially в _card_to_lot; DOM-regex остаётся fallback'ом.
4 offer-path + 5 area-полей (incl nested {value}). 6 тестов.

Часть B (#23, cian buildingCadastralNumber): УЖЕ реализовано — cian.py:291
читает offer.buildingCadastralNumber, :456 пишет в ScrapedLot, поле base.py:109
существует, тест test_cian_serp_cadastral_numbers зелёный. Изменений не нужно
(премиса аудита устарела). cian.py НЕ трогался.

ruff clean; pytest -k 'yandex or cian' 365 pass (+1 pre-existing unrelated fail
test_cian_valuation low_price mock, не из этого PR).
bot-reviewer approved these changes 2026-05-30 16:38:01 +00:00
bot-reviewer left a comment
Collaborator

APPROVE

Yandex area из embedded JSON state (#775 Part A).

Correctness

  • _extract_offer_areas_from_state: парсит initial_state_script / application/json, json.loads с try/except → {} на ошибке (best-effort, без краша). Обходит 4 offer-path (offers / pageData.offers / listing.offers / serpData.items), flatten одного уровня вложенности, читает offerId/id + area через _read_area_field (5 имён полей + nested {value} + float>0). Устойчиво к вариантам shape.
  • _card_to_lot: area_m2 = state_areas.get(offer_id) primary, DOM-regex fallback при None → existing-поведение при отсутствии state (без регрессии). State извлекается раз на страницу (эффективно). String-ключи offer_id согласованы.
  • Part B (cian cadastral): worker нашёл уже реализованным (cian.py:291/456, поле в base.py, тест зелёный) — устаревшая премиса аудита (как с avito source_id в #761), cian.py корректно не тронут, задокументировано.

Tests — 6 новых (state-no-DOM, multi-offer, DOM-fallback, nested-path, area-dict, empty-no-script). 365 pass.

Advisory (non-blocking) — worker раскрыл 1 pre-existing unrelated fail test_cian_valuation::test_cache_hit (KeyError: low_price в mock-фикстуре) — НЕ из этого PR (cian_valuation не тронут); нужен отдельный bug-issue.

Scope/security — yandex_realty.py + тест; cian не тронут, нет secrets, нет self-extending триггера.

Verdict: APPROVE (Praktika #775 Part A). Area-coverage на re-scrape → qa.

<!-- gendesign-review-bot: sha=d03c96a verdict=approve --> ✅ APPROVE Yandex area из embedded JSON state (#775 Part A). **Correctness** - `_extract_offer_areas_from_state`: парсит `initial_state_script` / `application/json`, json.loads с try/except → `{}` на ошибке (best-effort, без краша). Обходит 4 offer-path (offers / pageData.offers / listing.offers / serpData.items), flatten одного уровня вложенности, читает offerId/id + area через `_read_area_field` (5 имён полей + nested `{value}` + float>0). Устойчиво к вариантам shape. - `_card_to_lot`: `area_m2 = state_areas.get(offer_id)` primary, DOM-regex fallback при None → existing-поведение при отсутствии state (без регрессии). State извлекается раз на страницу (эффективно). String-ключи offer_id согласованы. - **Part B (cian cadastral)**: worker нашёл уже реализованным (cian.py:291/456, поле в base.py, тест зелёный) — устаревшая премиса аудита (как с avito source_id в #761), cian.py корректно не тронут, задокументировано. **Tests** — 6 новых (state-no-DOM, multi-offer, DOM-fallback, nested-path, area-dict, empty-no-script). 365 pass. **Advisory (non-blocking)** — worker раскрыл 1 pre-existing unrelated fail `test_cian_valuation::test_cache_hit` (`KeyError: low_price` в mock-фикстуре) — НЕ из этого PR (cian_valuation не тронут); нужен отдельный bug-issue. **Scope/security** — yandex_realty.py + тест; cian не тронут, нет secrets, нет self-extending триггера. **Verdict: ✅ APPROVE** (Praktika #775 Part A). Area-coverage на re-scrape → qa.
bot-reviewer merged commit 30ae4841a4 into main 2026-05-30 16:38:13 +00:00
bot-reviewer deleted branch fix/775-embedded-state-parse 2026-05-30 16:38:13 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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#783
No description provided.