fix(tradein/domclick): отказ с кодом 401 классифицировался как причина неизвестной природы (#3196)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
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
CI Trade-In / backend-tests (pull_request) Successful in 4m55s

Замер на проде 2026-08-29: страница отказа ДомКлика при HTTP 401 — РОВНО
26 624 байта, байт в байт та же, что снималась 28.08 под кодом 403. Ни PoW,
ни QRATOR, ни капчи. Приходит одинаково и с валидной сохранённой сессией
(16 куков из domclick_session), и полностью анонимно — значит это не
«сессия отвергнута», а WAF-отказ с подменённым кодом ответа.

В таблице классификатора 401 не было (403/429 → platform, 5xx → infra),
поэтому все три пробы подряд дали ban_kind='unknown' — ровно то, что #3196
и должен был убрать. Механика диагноза при этом рабочая: статус доезжает от
page.goto до исключения целым, шов blocked.status = status подтверждён живым
401 на проде.

Правка доменная — в _ban_kind_of_block домкликового таска, а не в общей
scraper_kit.browser_fetcher.ban_kind_from_status: у других поставщиков 401
обычно значит «наша сессия протухла», это наша сторона, и метка 'platform'
там зря запустила бы ротацию IP (#2611).
This commit is contained in:
bot-backend 2026-08-29 12:19:18 +03:00
parent cdcb152d76
commit 663f426d4c
2 changed files with 20 additions and 4 deletions

View file

@ -101,7 +101,7 @@ from app.core.config import settings
from app.core.shutdown import shutdown_requested
from app.services import domclick_session as domclick_session_svc
from app.services import scrape_runs as runs_mod
from app.services.scrape_runs import BAN_KIND_UNKNOWN
from app.services.scrape_runs import BAN_KIND_PLATFORM, BAN_KIND_UNKNOWN
logger = logging.getLogger(__name__)
@ -164,17 +164,32 @@ def _warn_before_domclick_cookies_expire(db: Session, run_id: int) -> None:
)
# 401 у ДомКлика — тот же отказ площадки, что 403 (замер прод 2026-08-29): страница отказа
# при 401 РОВНО 26 624 байта, байт в байт совпадает со страницей `403 | Домклик`
# (снята 28.08.2026), без PoW/QRATOR/капчи. Приходит одинаково и с валидной сохранённой
# сессией (16 куков domclick_session), и полностью анонимно — значит это не «сессия
# отвергнута» (тогда анонимный запрос вёл бы себя иначе), а WAF-отказ с подменённым кодом.
# Правка ДОМЕННАЯ, не в общей ban_kind_from_status (scraper_kit.browser_fetcher): у других
# поставщиков 401 обычно значит «наша сессия протухла» (наша сторона) — общая метка
# 'platform' там зря запустила бы ротацию IP (#2611).
_DOMCLICK_REFUSAL_STATUSES = frozenset({401})
def _ban_kind_of_block(exc: DomClickBlockedError) -> str:
"""Диагноз одного блока по HTTP-статусу ответа площадки (#3196).
"""Диагноз одного блока по HTTP-статусу ответа площадки (#3196, #3178).
Форма повторяет avito_detail_backfill (ban_kind_of_exception Counter
mark_backfill_finished(ban_kinds=...)), но опора другая: у Авито причину несёт
ТИП исключения (AvitoSidecarUnavailableError vs AvitoBlockedError), у Домклика
тип один на оба случая, поэтому смотрим статус. 403/429 'platform', 5xx
тип один на оба случая, поэтому смотрим статус. 401/403/429 'platform' (401
доменное исключение из общего правила, см. _DOMCLICK_REFUSAL_STATUSES), 5xx
'infra' (ban_kind_from_status), статуса нет 'unknown' не назначаем причину,
которую не установили (#2764).
"""
return ban_kind_from_status(getattr(exc, "status", None)) or BAN_KIND_UNKNOWN
status = getattr(exc, "status", None)
if status in _DOMCLICK_REFUSAL_STATUSES:
return BAN_KIND_PLATFORM
return ban_kind_from_status(status) or BAN_KIND_UNKNOWN
@dataclass

View file

@ -119,6 +119,7 @@ def _ban_kinds_of(runs: MagicMock) -> Counter:
@pytest.mark.parametrize(
("status", "expected"),
[
(401, "platform"), # прод 28-29.08: страница отказа байт-в-байт как у 403
(403, "platform"), # ровно тот случай, что намеряли вживую: `403 | Домклик`
(429, "platform"),
(500, "infra"),