fix(scrapers): cian full_load anti-zombie + avito changeip-resilience & честный статус (#1949 #1950) #1952

Merged
lekss361 merged 2 commits from fix/scraper-resilience-cian-zombie-avito-changeip into main 2026-06-27 08:38:11 +00:00
Owner

Код-фиксы из диагностики скрейперов 2026-06-27. Только scrape_pipeline.py + config.py + тесты.

#1950 avito (commit 757f6486)

  1. changeip-resilience: _rotate_proxy_ip — одношотный 30s-GET заменён на retry-цикл (proxy_rotate_attempts=3 × proxy_rotate_attempt_timeout_s=8.0, пауза 1s, True на первой удаче). Чинит корень бана run 418: единичный curl-28 timeout changeip ронял весь run.
  2. честный статус: run_avito_city_sweep — когда SERP уже собрал лоты (lots_inserted+updated>0) а упали только detail/houses → mark_done (с нотой), mark_banned только при 0 лотов. Флаг avito_serp_ok_not_banned=True. (run 418 сохранил 99+152 строки под лейблом banned.)

#1949 cian full_load anti-zombie (commit e24d3cf4)

  1. per-fetch hard-cancel: _browser.fetch обёрнут в asyncio.wait_for(timeout=cian_full_load_per_fetch_timeout_s=90.0) (зависший browser-fetch отменяется → _fetch_page_html ловит → [] → gather завершается, не морозит run). Применяется к per-run CianScraper()._browser (свежий на каждый run, рвётся с async with → без утечки/вложенности).
  2. фоновый heartbeat: asyncio.create_task(_background_heartbeat()) обновляет heartbeat_at каждые 60s независимо от on_bucket → прогресс-run не выглядит zombie; отменяется в finally.

Новые settings (ENV-override, дефолты = безопасны)

proxy_rotate_attempt_timeout_s=8.0, proxy_rotate_attempts=3, avito_serp_ok_not_banned=True, cian_full_load_per_fetch_timeout_s=90.0.

Проверка

Полный pytest (не -k, урок test_849): 2434 passed, 2 skipped; +10 новых тестов (6 avito changeip-retry/status + 4 cian wait_for/heartbeat). 5 collection-errors в tests/scrapers/ — pre-existing (env-specific, не от этих правок; CI подтвердит). ruff clean.

NB: city_sweep watchdog (cian DORMANT) НЕ трогали — отдельно (#1949 part 2). Деплой: recreate tradein-scraper. Cross-ref #1949, #1950, #1790, #1848, диагностика 2026-06-27.

Код-фиксы из диагностики скрейперов 2026-06-27. Только `scrape_pipeline.py` + `config.py` + тесты. ## #1950 avito (commit `757f6486`) 1. **changeip-resilience**: `_rotate_proxy_ip` — одношотный 30s-GET заменён на retry-цикл (`proxy_rotate_attempts=3` × `proxy_rotate_attempt_timeout_s=8.0`, пауза 1s, True на первой удаче). Чинит корень бана run 418: единичный `curl-28` timeout changeip ронял весь run. 2. **честный статус**: `run_avito_city_sweep` — когда SERP уже собрал лоты (`lots_inserted+updated>0`) а упали только detail/houses → `mark_done` (с нотой), `mark_banned` только при 0 лотов. Флаг `avito_serp_ok_not_banned=True`. (run 418 сохранил 99+152 строки под лейблом banned.) ## #1949 cian full_load anti-zombie (commit `e24d3cf4`) 3. **per-fetch hard-cancel**: `_browser.fetch` обёрнут в `asyncio.wait_for(timeout=cian_full_load_per_fetch_timeout_s=90.0)` (зависший browser-fetch отменяется → `_fetch_page_html` ловит → `[]` → gather завершается, не морозит run). Применяется к **per-run** `CianScraper()._browser` (свежий на каждый run, рвётся с `async with` → без утечки/вложенности). 4. **фоновый heartbeat**: `asyncio.create_task(_background_heartbeat())` обновляет `heartbeat_at` каждые 60s независимо от on_bucket → прогресс-run не выглядит zombie; отменяется в `finally`. ## Новые settings (ENV-override, дефолты = безопасны) `proxy_rotate_attempt_timeout_s=8.0`, `proxy_rotate_attempts=3`, `avito_serp_ok_not_banned=True`, `cian_full_load_per_fetch_timeout_s=90.0`. ## Проверка **Полный** `pytest` (не -k, урок test_849): **2434 passed**, 2 skipped; +10 новых тестов (6 avito changeip-retry/status + 4 cian wait_for/heartbeat). 5 collection-errors в `tests/scrapers/` — pre-existing (env-specific, не от этих правок; CI подтвердит). ruff clean. NB: city_sweep watchdog (cian DORMANT) НЕ трогали — отдельно (#1949 part 2). Деплой: recreate tradein-scraper. Cross-ref #1949, #1950, #1790, #1848, диагностика 2026-06-27.
lekss361 added 2 commits 2026-06-27 08:36:16 +00:00
Два связанных фикса из диагностики 2026-06-27:

ФИКС A (_rotate_proxy_ip): вместо одношотного GET с timeout=30s — retry-loop
(proxy_rotate_attempts=3 попыток по proxy_rotate_attempt_timeout_s=8s каждая).
Зависший changeip-сервис больше не блокирует весь sweep на 30s.
Первый успешный attempt → True; все провалились → False (логируется как error).

ФИКС B (run_avito_city_sweep): когда SERP-фаза уже сохранила лоты
(lots_inserted + lots_updated > 0) и упала только detail/houses-фаза
(AvitoBlockedError / RateLimitedError), помечаем run как done (не banned)
при avito_serp_ok_not_banned=True (default). Метрика banned зарезервирована
для случаев где SERP сам заблокирован (0 лотов).

Новые настройки (ENV):
  PROXY_ROTATE_ATTEMPT_TIMEOUT_S=8.0   — timeout одной changeip-попытки
  PROXY_ROTATE_ATTEMPTS=3              — число попыток перед отказом
  AVITO_SERP_OK_NOT_BANNED=true        — флаг нового поведения (default on)
fix(scrapers): cian full_load anti-zombie — per-fetch wait_for + heartbeat внутри бакета (#1949)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
e24d3cf4be
Два связанных фикса из диагностики 2026-06-27:

ФИКС C (run_cian_full_load, per-fetch timeout): monkey-patch _browser.fetch →
asyncio.wait_for(_f(url), timeout=cian_full_load_per_fetch_timeout_s=90s).
BrowserFetcher.fetch имеет httpx-timeout 120s, но browser-сервис иногда виснет
так что httpx не получает ответ (нет EOF) → fetch висит → asyncio.gather
блокирует весь bucket → heartbeat_at не обновляется → reap_zombies убивает живой run.
wait_for(90s) отменяет зависший fetch → TimeoutError → _fetch_page_html ловит
как Exception → None → _one_page → [] → gather завершается нормально.
За флагом (cian_full_load_per_fetch_timeout_s=0 = отключить, >0 = включить).

ФИКС D (background heartbeat): asyncio.create_task(_background_heartbeat())
обновляет heartbeat_at каждые 60s независимо от on_bucket/on_progress прогресса.
Без него пустые bucket или зависший gather замораживает heartbeat на несколько часов.
Task отменяется в finally → не течёт при return/raise/cancel.

Новая настройка (ENV):
  CIAN_FULL_LOAD_PER_FETCH_TIMEOUT_S=90.0  — timeout одного browser-fetch
lekss361 merged commit 1123f38149 into main 2026-06-27 08:38:11 +00:00
lekss361 deleted branch fix/scraper-resilience-cian-zombie-avito-changeip 2026-06-27 08:38:11 +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#1952
No description provided.