gendesign/tradein-mvp/packages/scraper-kit/src/scraper_kit/avito_exceptions.py
bot-backend 9e5e9fca08
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m58s
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / deploy (push) Successful in 1m44s
fix(tradein/scraper): статус «забанен» перестаёт выдавать наш сбой за чужой (#2686) (#2711)
2026-08-06 08:18:26 +00:00

64 lines
3.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Avito-specific exceptions для anti-bot detection."""
from scraper_kit.proxy_errors import ProxyBanError
class AvitoError(Exception):
"""Base для всех Avito scraper exceptions."""
class AvitoBlockedError(AvitoError, ProxyBanError):
"""HTTP 403 от Avito — IP-level block detected.
Дополнительно наследует `ProxyBanError` (#2600 п.1) — генерик curl-путь
(`providers/_proxy.py::curl_proxy_url`) распознаёт это как «площадка забанила
прокси» через `isinstance(exc, ProxyBanError)`, не импортируя avito_exceptions
напрямую. `except AvitoBlockedError:` во всём остальном коде не меняется —
mixin ничего не убирает, только добавляет.
"""
class AvitoRateLimitedError(AvitoError):
"""HTTP 429 от Avito — rate limit triggered."""
class AvitoSidecarUnavailableError(AvitoRateLimitedError):
"""НАШ браузерный сайдкар не отдал страницу — площадка ни при чём (#2686).
Поднимается только там, где отказ ПОРОЖДЁН нашей инфраструктурой: sidecar
вернул 503 «browser unavailable», упал внутри (Page.goto Timeout, браузер не
поднялся) либо соединение до него оборвалось (ReadTimeout/ConnectError). Это
отличается от AvitoBlockedError (площадка показала firewall) и от честного
HTTP 429 самой площадки, хотя все три раньше давали ОДИН статус 'banned'
92 прогона из 114 (81%) за 45 суток были нашим сайдкаром, и по этому статусу
приняли решение замедлить сбор вдвое (миграция 206).
Наследует AvitoRateLimitedError НАМЕРЕННО: всё поведение прогона обязано
остаться прежним (`except (AvitoBlockedError, AvitoRateLimitedError)` в
pipeline/full-load/backfill ловят его без изменений → mark_banned →
done_buckets-чекпоинт сохраняется). Разводятся не пути, а ДИАГНОЗ:
pipeline пишет его в scrape_runs.ban_kind ('infra' vs 'platform').
"""
class AvitoListingGoneError(AvitoError):
"""Объявление удалено / вернуло 404 (страница «Ошибка 404» вместо item-view).
Отдельный сигнал от AvitoBlockedError/AvitoRateLimitedError: 404 — НЕ анти-бот
deflect, а мёртвый листинг (его незачем ротировать/ретраить). Наследует общий
AvitoError, но НЕ AvitoBlockedError — чтобы backfill-ловушка
`except (AvitoBlockedError, AvitoRateLimitedError)` его НЕ перехватывала и не крутила
consecutive-block breaker на мёртвых координатных дырах (run 458: 5 dead → abort).
"""
class AvitoContentBlockedError(AvitoBlockedError):
"""HTTP 200 от Avito, но 0 карточек на первой странице — content-block / captcha.
Наследует AvitoBlockedError, чтобы существующие except-блоки на AvitoBlockedError
продолжали ловить этот сигнал (mark_banned, pipeline abort и т.д.).
"""
class AvitoParseError(AvitoError):
"""Cannot parse Avito HTML structure (selector changes)."""