fix(tradein): avito house-link из detail SSR — SERP-карточка JS-only (#871) #873

Merged
bot-reviewer merged 1 commit from fix/871-avito-house-link-from-detail into main 2026-05-31 08:27:48 +00:00
Owner

Summary

Fixes #871. Avito house-enrich был мёртв (unique_houses=0 всегда), т.к. house-link на SERP-карточке рендерится client-side JS → в SSR-HTML (curl_cffi) его нет. Verified live 2026-05-31 (Playwright): SERP SSR 0/41 vs rendered DOM 21/60; detail-страница в SSR содержит /catalog/houses/.../142792 + __preloadedState__; парсер house-каталога рабочий (/scrape/avito-house → house_id 372289).

Фикс берёт house_url из detail-страницы (которую и так фетчим для top-N).

  • avito_detail.py: house_catalog_url extraction — fallback с nd-jk-details-button (новостройки) на любой a[href*='/catalog/houses/'] (вторичка) + strip ?context.
  • scrape_pipeline.py: Step 5b — энричим дома, найденные через detail-страницы (дедуп против SERP-derived Step 4).
  • test: secondary-маркер без nd-jk + query strip.

Разблокирует #562 (avito house batch enrichment).

Test plan

  • uv run ruff check clean; uv run pytest -k 'detail or house' → 297 passed
  • code-reviewer: APPROVE (no blockers)
  • Post-deploy QA: trigger /scrape/avito-city-sweep (pages=1) → scrape_runs.counters.unique_houses > 0 (было 0)

NB: rate-limit/rotation detail-пути (detail без IP-ротации кладёт sweep на первом 429) — ОТДЕЛЬНАЯ задача, не в этом PR.

## Summary Fixes #871. Avito house-enrich был мёртв (`unique_houses=0` всегда), т.к. house-link на SERP-карточке рендерится client-side JS → в SSR-HTML (curl_cffi) его нет. **Verified live 2026-05-31** (Playwright): SERP SSR 0/41 vs rendered DOM 21/60; detail-страница в SSR содержит `/catalog/houses/.../142792` + `__preloadedState__`; парсер house-каталога рабочий (`/scrape/avito-house` → house_id 372289). Фикс берёт house_url из detail-страницы (которую и так фетчим для top-N). - `avito_detail.py`: `house_catalog_url` extraction — fallback с `nd-jk-details-button` (новостройки) на любой `a[href*='/catalog/houses/']` (вторичка) + strip `?context`. - `scrape_pipeline.py`: **Step 5b** — энричим дома, найденные через detail-страницы (дедуп против SERP-derived Step 4). - test: secondary-маркер без nd-jk + query strip. Разблокирует #562 (avito house batch enrichment). ## Test plan - [x] `uv run ruff check` clean; `uv run pytest -k 'detail or house'` → 297 passed - [x] code-reviewer: ✅ APPROVE (no blockers) - [ ] Post-deploy QA: trigger `/scrape/avito-city-sweep` (pages=1) → `scrape_runs.counters.unique_houses > 0` (было 0) NB: rate-limit/rotation detail-пути (detail без IP-ротации кладёт sweep на первом 429) — ОТДЕЛЬНАЯ задача, не в этом PR.
lekss361 added 1 commit 2026-05-31 08:24:47 +00:00
SERP-карточка отдаёт house-link («узнать больше о доме») только после
client-side JS → в SSR-HTML, который тянет curl_cffi, его нет (verified
live: SERP SSR 0/41 vs rendered DOM 21/60) → avito house-enrich мёртв
(unique_houses=0 всегда). Detail-страница в SSR house-link содержит, а её
мы и так фетчим для top-N.

- avito_detail.py: house_catalog_url extraction fallback с nd-jk-details-button
  (новостройки) на любой a[href*='/catalog/houses/'] (вторичка) + strip ?context.
- scrape_pipeline.py: Step 5b — энричим дома, найденные через detail-страницы
  (дедуп против SERP-derived Step 4).
- test: secondary-маркер без nd-jk + query strip.

Closes #871
bot-reviewer approved these changes 2026-05-31 08:27:38 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — avito house-link из detail SSR (#871), разблокирует #562.

Корень верный (verified live в PR): house-link на SERP-карточке рендерится JS → в curl_cffi SSR его нет (unique_houses=0 всегда). Detail-страница (которую и так фетчим для top-N) содержит /catalog/houses/... в SSR. Фикс берёт house_url оттуда.

Cruxes проверены:

  • avito_detail.py: nd-jk-details-button (новостройки) → fallback a[href*='/catalog/houses/'] (вторичка); urljoin(AVITO_BASE, urlparse(href).path) отбрасывает ?context — path и есть house-identity, корректно для обоих типов. Тест на secondary-маркер + query-strip.
  • scrape_pipeline.py Step 5b: urlparse импортирован (:31); дедуп detail_house_paths - unique_house_paths (:192) → нет двойного энрича Step 4; fetch_house_catalog(house_path, cffi_session=session) совпадает с сигнатурой (avito_houses.py:677); save_house_catalog_enrichmenthouse_id; block→raise (как Step 4), прочее→houses_failed.
  • touched_house_ids: инициализирован безусловно (pre-existing pipeline:done logger ссылается на него — иначе уже-merged #861 падал бы) → Step 5b .add безопасен.

297 тестов pass, own code-reviewer . Rate-limit/IP-rotation detail-пути честно вынесен out-of-scope (block→propagate = fail-safe). Self-extending/секретов нет, psycopg-SQL в диффе нет.

Мержу. #871 → qa (smoke: /scrape/avito-city-sweep pages=1unique_houses > 0).

<!-- gendesign-review-bot: sha=f65c334 verdict=approve --> ✅ **APPROVE** — avito house-link из detail SSR (#871), разблокирует #562. **Корень верный (verified live в PR):** house-link на SERP-карточке рендерится JS → в curl_cffi SSR его нет (`unique_houses=0` всегда). Detail-страница (которую и так фетчим для top-N) содержит `/catalog/houses/...` в SSR. Фикс берёт house_url оттуда. **Cruxes проверены:** - `avito_detail.py`: `nd-jk-details-button` (новостройки) → fallback `a[href*='/catalog/houses/']` (вторичка); `urljoin(AVITO_BASE, urlparse(href).path)` отбрасывает `?context` — path и есть house-identity, корректно для обоих типов. Тест на secondary-маркер + query-strip. - `scrape_pipeline.py` Step 5b: `urlparse` импортирован (:31); дедуп `detail_house_paths - unique_house_paths` (:192) → нет двойного энрича Step 4; `fetch_house_catalog(house_path, cffi_session=session)` совпадает с сигнатурой (avito_houses.py:677); `save_house_catalog_enrichment` → `house_id`; block→`raise` (как Step 4), прочее→`houses_failed`. - `touched_house_ids`: инициализирован безусловно (pre-existing `pipeline:done` logger ссылается на него — иначе уже-merged #861 падал бы) → Step 5b `.add` безопасен. 297 тестов pass, own code-reviewer ✅. Rate-limit/IP-rotation detail-пути честно вынесен out-of-scope (block→propagate = fail-safe). Self-extending/секретов нет, psycopg-SQL в диффе нет. Мержу. #871 → qa (smoke: `/scrape/avito-city-sweep pages=1` → `unique_houses > 0`).
bot-reviewer merged commit 70838c7dfe into main 2026-05-31 08:27:48 +00:00
bot-reviewer deleted branch fix/871-avito-house-link-from-detail 2026-05-31 08:27:48 +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#873
No description provided.