fix(tradein/cian): 403 снимает узел с выдачи, а не гаснет в return None (#2700) #2821
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2821
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2700-cian-detail-403"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что установлено пробой, а не рассуждением
Различающая проба на проде 2026-08-10 11:0x UTC: один и тот же detail-URL
(
ekb.cian.ru/sale/flat/332775238/), один и тот же код (build_curl_cffi_session+те же заголовки, что в боевом
providers/cian/detail.py), менялся толькопрокси-узел. 4 запроса, пауза 4 с.
defaultStatecian_waf_blockОтбита пара «узел × Циан», а не площадка. И не наш запрос: 200 пришли без
единой куки — detail-страница Циана авторизации не требует, к протухшим 30.06
кукам эти 403 отношения не имели.
Почему это держалось 15 суток
403 умирал внутри провайдера в
return None. Наружу не выходило ничего:curl_proxy_urlвидел чистый выход блока →mark_health(ok=True)на отбитый узел→ пул продолжал выдавать его Циану;
pipelineсчиталdetail_failed, но неerrors_count(тот растёттолько на исключении) →
detail=0/50иerrors=0в одной строке лога;mark_doneписалdone.Правки
providers/cian/detail.py: HTTP 403 →CianBlockedError(новыйcian_exceptions.py, наследникProxyBanError— тот же приём, что уAvitoBlockedError/DomClickBlockedError). Generic curl-слой сам делаетmark_bannedна пару «узел × cian», оба вызывающих уже считают исключение вerrors_count, ротация/abort фазы работают как у соседей. 404 баном несчитается — снятое объявление не должно снимать с выдачи здоровый узел.
Маркеры страницы намеренно НЕ проверяются: список маркеров объясняет уже
случившийся отказ и молчит про неизвестный (урок #2767 от 09.08).
scrape_runs.mark_done(обе копии, app + kit): прогон, у которого отказалакаждая попытка целой фазы (порог 3 попытки), больше не
done. Пары<phase>_attempted/<phase>_failedищутся в самих counters, а не по зашитомусписку фаз — список это ровно то место, куда забывают дописать новую фазу.
Статус
failedбез диагноза (кто виноват — не установлено), как в #2625/#2764.Замер влияния (прод, 90 суток, до мержа)
Правило переводит в
failed52 прогона из 3 292done(1.6%): 33cian_city_sweep*(detail 26 + houses 15, часть попадает по обеим фазам) и 11avito_city_sweep*— про последние никто не знал. Порог в 3 попытки отсекает 2прогона с единственной попыткой. Остальные 3 240 остаются
done.Ожидаемо и намеренно: пока фаза
housesу cian-развёрток отказывает 10 из 10(это #2767, сегодняшний прогон 3597 — тоже 10/10), ежедневный
cian_city_sweepбудет
failed. Это и есть предмет задачи: направление работы мертво, а суммарныйсбор 1 651 лота его прятал.
Test plan
tests/test_2700_cian_detail_403_node.py— красный на старом коде в трёхутверждениях: узел не банился (
mark_bannedне звался,fetch_detailвернулNone) и оба финализатора писали
doneпрод-прогону 3258 (detail 50/50 отказ).Прогон на origin/main (с одним лишь добавленным
cian_exceptions.py, чтобыкраснело поведение, а не отсутствие имени):
3 failed, 8 passed. На ветке —11 passed.4150 passed, 18 skipped.Критерий приёмки, записанный ДО факта
A. Честность статуса — ближайший
cian_city_sweep, 2026-08-11 ~03:00-05:30 UTC.Принято, если прогон, у которого
houses_failed = houses_attempted >= 3(или то жепо detail), имеет
status='failed'иerrorначинается сphase-honest-status:.Опровергнуто, если такой прогон снова
done— значит правка не доехала до образалибо проводка оборвана.
B. Снятие отбитого узла — не раньше 2026-08-12 08:14 UTC (сейчас узел 1 и так
вне выдачи Циану: проба #2803 забанила пару до этого времени).
Принято, если после первого же 403 боевого сбора появляется строка с
reason='banned:cian'(неprobe:browser), а в counters того прогонаdetail_failed≤ 3 приdetail_attemptedв разы больше. Опровергнуто, если сноваdetail_failed = detail_attemptedбез строки бана.Что этой правкой НЕ чинится (уходит владельцу)
Седьмой источник эстиматора (
cian_valuation) иcian_history_backfillстоят накуках, и это не прокси: та же проба со стороны здорового узла 10 отдала
HTTP 200 без маркеров и
isAuthenticated=false— сессия мертва на сторонеЦиана, а не по нашему TTL. Требуется вход владельца; цена простоя — 320 оценок,
выданных с 29.06 без седьмого источника, и 42 суток без
cian_history_backfill.Refs #2700
errors=0иdone, все домовые поля Циана нулевые #2700errors=0иdone, все домовые поля Циана нулевые #2700errors=0иdone, все домовые поля Циана нулевые #2700