[tradein][backend] N1 parser: извлекать floor/total_floors (сейчас 0%) #795

Closed
opened 2026-05-30 17:12:13 +00:00 by bot-backend · 2 comments
Collaborator

Worker: мелкая доработка существующего парсера. Привязка к #575 (N1 integration).

Проблема

listings source='n1': floor=0%, total_floors=0%. Причина — n1.py их не извлекает (не баг разметки). Проба 2026-05-30: N1 SERP жив (SSR, 25 карточек, data-test="offers-list-item"), этаж присутствует в тексте карточки префиксом "N/M" (напр. "1/11 2-к, Репина, 75/2 стр." → этаж 1 из 11).

Задача

В tradein-mvp/backend/app/services/scrapers/n1.py_card_to_lot:

  • извлечь floor/total_floors из ведущего токена N/M в тексте карточки/ссылки (до сегмента комнат).
  • listing_date на SERP N1 НЕ показывается (подтверждено пробой) → не трогать, оставить None.

Files

  • tradein-mvp/backend/app/services/scrapers/n1.py (parser 137-202, helpers 205+).
  • Фикстура для unit-теста (создать/обновить из реальной карточки).

DoD

  • floor/total_floors заполняются у n1-карточек где есть N/M.
  • uv run ruff check app/services/scrapers/n1.py clean.
  • unit-тест на фикстуре → floor != None.

Не делать

  • НЕ трогать area/price/rooms/address (работают, 100%). Объём n1 мал (~264, 1.4%) — низкий приоритет.

Связано

#575.

> Worker: мелкая доработка существующего парсера. Привязка к #575 (N1 integration). ## Проблема `listings` source='n1': `floor`=0%, `total_floors`=0%. Причина — `n1.py` их **не извлекает** (не баг разметки). Проба 2026-05-30: N1 SERP жив (SSR, 25 карточек, `data-test="offers-list-item"`), этаж **присутствует** в тексте карточки префиксом `"N/M"` (напр. `"1/11 2-к, Репина, 75/2 стр."` → этаж 1 из 11). ## Задача В `tradein-mvp/backend/app/services/scrapers/n1.py` → `_card_to_lot`: - извлечь `floor`/`total_floors` из ведущего токена `N/M` в тексте карточки/ссылки (до сегмента комнат). - `listing_date` на SERP N1 НЕ показывается (подтверждено пробой) → не трогать, оставить None. ## Files - `tradein-mvp/backend/app/services/scrapers/n1.py` (parser 137-202, helpers 205+). - Фикстура для unit-теста (создать/обновить из реальной карточки). ## DoD - `floor`/`total_floors` заполняются у n1-карточек где есть `N/M`. - `uv run ruff check app/services/scrapers/n1.py` clean. - unit-тест на фикстуре → floor != None. ## Не делать - НЕ трогать area/price/rooms/address (работают, 100%). Объём n1 мал (~264, 1.4%) — низкий приоритет. ## Связано #575.
bot-backend added the
priority/p3
scope/backend
tradein
labels 2026-05-30 17:12:13 +00:00
lekss361 added this to the Praktika demo — Mon 2026-06-01 milestone 2026-05-30 17:21:06 +00:00
bot-backend self-assigned this 2026-05-30 17:24:26 +00:00
bot-backend added the
status/wip
label 2026-05-30 17:24:27 +00:00
bot-backend added
status/review
and removed
status/wip
labels 2026-05-30 17:28:18 +00:00
bot-reviewer added
status/qa
and removed
status/review
labels 2026-05-30 17:31:20 +00:00
Collaborator

QA PASS — PR #802 (merged 17:31:13). DoD здесь чисто code+unit (не 24h-gated, в отличие от data-acceptance #575) → верифицировано по merged diff.

Parser-логика (n1.py):

  • _extract_floor_from_title — anchored regex ^\s*(\d{1,3})\s*/\s*(\d{1,3})\b: ловит только ведущий токен «N/M», дробь номера дома в середине («Репина, 75/2») не совпадает (нет ^). Sanity: floor≥1, total≥floor, total≤100 → иначе (None, None).
  • Проброшено в _card_to_lotfloor/total_floors на ScrapedLot.
  • listing_date оставлен None (на SERP N1 даты нет — подтверждено).

Unit-тесты (test_n1_floor_parse.py, pure-function — без сети/БД):

  • happy: 5 кейсов (вкл. 17/17 верхний, 2/100 край диапазона).
  • none: 8 кейсов — house-fraction 75/2 (не матчит), 0/9 (floor=0), 9/5 (total<floor), 2/101 (>100), пустая строка, не-leading дробь.
  • integration: _card_to_lot через fake-node → floor=1/total=11; None при отсутствии токена; listing_date=None.

DoD «floor/total_floors заполняются у n1-карточек где есть N/M» доказан на уровне парсера; фактический fill в listings подтвердится вместе с N1-sweep прогоном (#575, 24h-gated).


ℹ️ Non-blocking наблюдение (pre-existing, ВНЕ скоупа #795 — не фейлю): address-strip regex ^\s*(\d+-к|студи[яи])... НЕ снимает ведущий «N/M» — при реальном title «1/11 2-к, Репина...» префикс комнат стоит после 1/11, поэтому ^\d+-к не матчит и адрес может остаться как «1/11 2-к, Репина, 75/2 стр.» (комментарий в diff обновлён, но сама regex — нет). Если geocode по такому адресу шумит — это #575-level фоллоу-ап (rooms через search работает, поэтому acceptance #575 «address 100%» мог это не поймать). Отдаю аналитику/воркеру на оценку, отдельный issue не завожу (неуверенность + pre-existing).

status/done, closing.

✅ **QA PASS** — PR #802 (merged 17:31:13). DoD здесь чисто code+unit (не 24h-gated, в отличие от data-acceptance #575) → верифицировано по merged diff. **Parser-логика (`n1.py`):** - `_extract_floor_from_title` — anchored regex `^\s*(\d{1,3})\s*/\s*(\d{1,3})\b`: ловит **только ведущий** токен «N/M», дробь номера дома в середине («Репина, 75/2») не совпадает (нет `^`). Sanity: floor≥1, total≥floor, total≤100 → иначе `(None, None)`. ✅ - Проброшено в `_card_to_lot` → `floor`/`total_floors` на `ScrapedLot`. ✅ - `listing_date` оставлен None (на SERP N1 даты нет — подтверждено). ✅ **Unit-тесты (`test_n1_floor_parse.py`, pure-function — без сети/БД):** - happy: 5 кейсов (вкл. `17/17` верхний, `2/100` край диапазона). ✅ - none: 8 кейсов — house-fraction `75/2` (не матчит), `0/9` (floor=0), `9/5` (total<floor), `2/101` (>100), пустая строка, не-leading дробь. ✅ - integration: `_card_to_lot` через fake-node → floor=1/total=11; None при отсутствии токена; listing_date=None. ✅ DoD «floor/total_floors заполняются у n1-карточек где есть N/M» доказан на уровне парсера; фактический fill в `listings` подтвердится вместе с N1-sweep прогоном (#575, 24h-gated). --- ℹ️ **Non-blocking наблюдение** (pre-existing, ВНЕ скоупа #795 — не фейлю): address-strip regex `^\s*(\d+-к|студи[яи])...` НЕ снимает ведущий «N/M» — при реальном title `«1/11 2-к, Репина...»` префикс комнат стоит после `1/11`, поэтому `^\d+-к` не матчит и адрес может остаться как `«1/11 2-к, Репина, 75/2 стр.»` (комментарий в diff обновлён, но сама regex — нет). Если geocode по такому адресу шумит — это #575-level фоллоу-ап (rooms через `search` работает, поэтому acceptance #575 «address 100%» мог это не поймать). Отдаю аналитику/воркеру на оценку, отдельный issue не завожу (неуверенность + pre-existing). → `status/done`, closing.
bot-qa added
status/done
and removed
status/qa
labels 2026-05-30 17:37:39 +00:00
Author
Collaborator

WONTFIX 2026-05-30 — N1 переехал в Cian (ekaterinburg.n1.ru → cian), источник мёртв. n1-скрейпер обновлять не нужно. scrape_schedules id=109 (n1_city_sweep) выключен. Покрытие cian обеспечит #806 (proxy) + #639 (куки).

WONTFIX 2026-05-30 — **N1 переехал в Cian** (ekaterinburg.n1.ru → cian), источник мёртв. n1-скрейпер обновлять не нужно. `scrape_schedules` id=109 (n1_city_sweep) выключен. Покрытие cian обеспечит #806 (proxy) + #639 (куки).
Sign in to join this conversation.
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#795
No description provided.