"""Avito-specific exceptions для anti-bot detection.""" class AvitoError(Exception): """Base для всех Avito scraper exceptions.""" class AvitoBlockedError(AvitoError): """HTTP 403 от Avito — IP-level block detected.""" class AvitoRateLimitedError(AvitoError): """HTTP 429 от Avito — rate limit triggered.""" 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)."""