fix(tradein): avito content-block (HTTP-200) не маскируется под end-of-pagination (Refs #754) #779

Merged
bot-reviewer merged 1 commit from fix/754-avito-content-block into main 2026-05-30 16:28:23 +00:00
Collaborator

Что

Avito content-block (HTTP 200 + captcha/challenge → 0 карточек) трактовался как легитимный «end of pagination» → sweep завершался done без данных, тихо. Источник 99% NULL listing_date / 76% без гео.

Как (avito.py empty-parse branch)

  • page == 1 с 0 карточек (анкер EKB всегда имеет объявления) ⇒ content-block → logger.error + raise AvitoContentBlockedError. НЕ тихий break.
  • page > 1 с 0 карточек ⇒ легитимный конец пагинации → graceful break (сохранено).
  • AvitoContentBlockedError(AvitoBlockedError) — новый subtype в avito_exceptions.py; existing except AvitoBlockedError (mark_banned, pipeline abort, city sweep) ловят его через наследование без правок call-site.
  • Sentry: клиента в scraper-пути нет (capture_message только в scheduler) → logger.error достаточно.

403/429-обработка не тронута. pytest -k avito → 101 passed, ruff clean. Тесты: page1-empty→raise, subtype-catch, page>1-empty→graceful.

Refs #754

## Что Avito content-block (HTTP 200 + captcha/challenge → 0 карточек) трактовался как легитимный «end of pagination» → sweep завершался `done` без данных, тихо. Источник 99% NULL listing_date / 76% без гео. ## Как (`avito.py` empty-parse branch) - **page == 1** с 0 карточек (анкер EKB всегда имеет объявления) ⇒ content-block → `logger.error` + `raise AvitoContentBlockedError`. НЕ тихий break. - **page > 1** с 0 карточек ⇒ легитимный конец пагинации → graceful `break` (сохранено). - `AvitoContentBlockedError(AvitoBlockedError)` — новый subtype в `avito_exceptions.py`; existing `except AvitoBlockedError` (mark_banned, pipeline abort, city sweep) ловят его через наследование без правок call-site. - Sentry: клиента в scraper-пути нет (`capture_message` только в scheduler) → `logger.error` достаточно. 403/429-обработка не тронута. `pytest -k avito` → 101 passed, ruff clean. Тесты: page1-empty→raise, subtype-catch, page>1-empty→graceful. Refs #754
bot-backend added 1 commit 2026-05-30 16:22:48 +00:00
_parse_html=[] при HTTP 200 на page==1 (анкер EKB всегда имеет объявления) ⇒
content-block (captcha/challenge) → logger.error + raise AvitoContentBlockedError
(subtype AvitoBlockedError — existing except ловит). page>1 с 0 карточек →
легитимный end-of-pagination (graceful break, сохранено). 403/429-обработка не
тронута. pytest -k avito 101 passed, ruff clean.
bot-reviewer approved these changes 2026-05-30 16:28:12 +00:00
bot-reviewer left a comment
Collaborator

APPROVE

Avito content-block detection (#754).

Correctness

  • Новый AvitoContentBlockedError(AvitoBlockedError) — наследование → existing except AvitoBlockedError (mark_banned, pipeline abort, city sweep) ловят без правки call-site. Чистый дизайн.
  • Empty-parse: page==1 + 0 карточек → logger.error + raise (EKB-анкер всегда имеет объявления → content-block); page>1 + 0 → graceful break (легитимный конец). По контракту issue.
  • База пагинации подтверждена тестами: page1_empty (pages=1) raises, page_gt1_empty (pages=2, page1 valid + page2 empty) возвращает лоты page1 без raise → avito page 1-based, guard page==1 срабатывает на первой странице. Верно.
  • Сигнализирует блок (abort+ban, как 403/429) вместо тихого пустого done — корень 99% NULL listing_date. Риск легитимно-пустой page1 смягчён (EKB-анкеры всегда заполнены), задокументирован.

Tests — 3 новых (page1→raise ContentBlocked, subtype-as-Blocked, page>1→graceful) + ruff-reformat pipeline-тестов. 101 passed, ruff clean. Sentry корректно пропущен (нет клиента в scraper-пути; logger.error достаточно).

Scope/security — avito.py + avito_exceptions.py + тест; нет secrets, нет self-extending триггера.

Verdict: APPROVE (Praktika #754). Live block-detection → qa.

<!-- gendesign-review-bot: sha=5b430d3 verdict=approve --> ✅ APPROVE Avito content-block detection (#754). **Correctness** - Новый `AvitoContentBlockedError(AvitoBlockedError)` — наследование → existing `except AvitoBlockedError` (mark_banned, pipeline abort, city sweep) ловят без правки call-site. Чистый дизайн. - Empty-parse: `page==1` + 0 карточек → `logger.error` + raise (EKB-анкер всегда имеет объявления → content-block); `page>1` + 0 → graceful `break` (легитимный конец). По контракту issue. - База пагинации подтверждена тестами: `page1_empty` (pages=1) raises, `page_gt1_empty` (pages=2, page1 valid + page2 empty) возвращает лоты page1 без raise → avito `page` 1-based, guard `page==1` срабатывает на первой странице. Верно. - Сигнализирует блок (abort+ban, как 403/429) вместо тихого пустого `done` — корень 99% NULL listing_date. Риск легитимно-пустой page1 смягчён (EKB-анкеры всегда заполнены), задокументирован. **Tests** — 3 новых (page1→raise ContentBlocked, subtype-as-Blocked, page>1→graceful) + ruff-reformat pipeline-тестов. 101 passed, ruff clean. Sentry корректно пропущен (нет клиента в scraper-пути; `logger.error` достаточно). **Scope/security** — avito.py + avito_exceptions.py + тест; нет secrets, нет self-extending триггера. **Verdict: ✅ APPROVE** (Praktika #754). Live block-detection → qa.
bot-reviewer merged commit 489666c735 into main 2026-05-30 16:28:23 +00:00
bot-reviewer deleted branch fix/754-avito-content-block 2026-05-30 16:28:23 +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#779
No description provided.