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
64 lines
3.6 KiB
Python
64 lines
3.6 KiB
Python
"""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)."""
|