Авито: добор карточек больше не сжигает 1600 попыток на упавшем сайдкаре и везде ходит браузером #3560
4 changed files with 48 additions and 28 deletions
|
|
@ -1221,25 +1221,19 @@ class Settings(BaseSettings):
|
|||
cian_login_success_cookie: str = "DMIR_AUTH"
|
||||
cian_login_wait_ms: int = 4000
|
||||
|
||||
# detail_backfill через curl_cffi+backconnect (mproxy) вместо браузера/auv.
|
||||
# SERP (full_load/city_sweep) и detail_backfill делят один прокси-аккаунт auv
|
||||
# (~5 параллельных коннектов); browser-фетч в backfill открывает десятки коннектов
|
||||
# → cap превышается → HTTP 500 / краши. Backconnect (mproxy, авто-ротация,
|
||||
# 1 коннект/запрос) развязывает прокси-аккаунты.
|
||||
# True (дефолт) = curl_cffi через settings.scraper_proxy_url (backconnect mproxy).
|
||||
# False = старое browser-поведение (BrowserFetcher/auv, как scraper_fetch_mode).
|
||||
# ENV: AVITO_DETAIL_BACKFILL_USE_CURL.
|
||||
# detail_backfill через curl_cffi вместо браузера. ENV: AVITO_DETAIL_BACKFILL_USE_CURL.
|
||||
# False (дефолт) = BrowserFetcher (camoufox дожидается QRATOR PoW, #3045/#3046).
|
||||
# True = curl_cffi через settings.scraper_proxy_url.
|
||||
#
|
||||
# ⚠️ Обоснование выше УСТАРЕЛО для Авито (замер 2026-08-21). Авито за QRATOR
|
||||
# отдаёт JS proof-of-work челлендж, который curl_cffi не решает: прогоны
|
||||
# 4348/4394/4508 — 3-4 обогащённых из 46-53 попыток (~6%) против ~74% на
|
||||
# браузерном пути. Опасение «browser превышает cap прокси-аккаунта auv» снято:
|
||||
# браузер сериализован BROWSER_CONCURRENCY=1 и ходит через тот же backconnect.
|
||||
# Прод переведён на браузер через docker-compose.prod.yml (environment
|
||||
# перекрывает env_file). Дефолт оставлен True, чтобы не менять поведение
|
||||
# других окружений вслепую.
|
||||
# Дефолт False (#3045): Авито за QRATOR отдаёт JS proof-of-work челлендж, который
|
||||
# curl_cffi не решает в принципе — замер 2026-08-21, прогоны 4348/4394/4508: 3-4
|
||||
# обогащённых из 46-53 попыток (~6%) против ~74% на браузере. Старое обоснование
|
||||
# curl (браузер превышал cap прокси-аккаунта auv) снято: браузер сериализован
|
||||
# BROWSER_CONCURRENCY=1. Пока дефолт был True, браузер включался только
|
||||
# environment'ом сервиса scraper, а tradein-backend (ручка админки
|
||||
# scrape_avito_detail_backfill исполняет добор в своём процессе) шёл curl'ом.
|
||||
avito_detail_backfill_use_curl: bool = Field(
|
||||
default=True, validation_alias="AVITO_DETAIL_BACKFILL_USE_CURL"
|
||||
default=False, validation_alias="AVITO_DETAIL_BACKFILL_USE_CURL"
|
||||
)
|
||||
|
||||
# #1950: hard-timeout на один detail-fetch внутри avito_detail_backfill. Зависший
|
||||
|
|
|
|||
|
|
@ -98,9 +98,9 @@ from app.services.scraper_adapters import RealProxyProvider, RealScraperConfig
|
|||
# принимает config: ScraperConfig | None (Strangler-инъекция #2330) и пробрасывает
|
||||
# его в _build_detail_session() для settings.scraper_proxy_url (sticky МГТС-прокси),
|
||||
# зеркаля fetch_detail. Оба call site'а ниже передают config=RealScraperConfig() —
|
||||
# без него warm-batch curl-путь (use_curl=True, ДЕФОЛТ прод-режима,
|
||||
# avito_detail_backfill_use_curl=True) молча терял бы backconnect (прямое
|
||||
# datacenter-подключение вместо sticky-прокси), тот же класс бага что #2322/#2310.
|
||||
# без него warm-batch curl-путь (use_curl=True, avito_detail_backfill_use_curl) молча
|
||||
# терял бы backconnect (прямое datacenter-подключение вместо sticky-прокси), тот же
|
||||
# класс бага что #2322/#2310.
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -232,12 +232,10 @@ async def _rotate_current_proxy(
|
|||
settings.scraper_proxy_url БЕЗ lease — там ротировать нечего, вызывающий цикл не
|
||||
зовёт эту функцию в том режиме вовсе.
|
||||
|
||||
Прод при этом в browser-режиме, а не в curl: у контейнера tradein-scraper (там же
|
||||
живёт планировщик) проверено AVITO_DETAIL_BACKFILL_USE_CURL=false при
|
||||
SCRAPER_FETCH_MODE=browser и USE_PROXY_POOL_BROWSER=true, так что ротация
|
||||
активна. Значение true стоит только у tradein-backend, который добор не запускает.
|
||||
Комментарий ниже по файлу (~строка 653) называет use_curl=True «прод-дефолтом» —
|
||||
это предсуществующее заблуждение, а не описание текущего прода.
|
||||
Прод в browser-режиме, а не в curl: дефолт avito_detail_backfill_use_curl=False
|
||||
(#3045). Добор исполняют ДВА процесса — планировщик в tradein-scraper и ручка
|
||||
админки scrape_avito_detail_backfill в tradein-backend, — поэтому режим задаётся
|
||||
дефолтом кода, а не environment'ом одного сервиса.
|
||||
|
||||
Отказ провайдера (лимит исчерпан, нет rotate_url, сетевой сбой, неизвестный хост)
|
||||
НЕ должен ронять прогон — логируем и продолжаем на текущем адресе. Эта попытка НЕ
|
||||
|
|
@ -705,7 +703,7 @@ async def run_avito_detail_backfill(
|
|||
# config.scraper_proxy_url=None для backconnect-gate'а
|
||||
# (elif not use_curl own-session path), теряя reconnect-
|
||||
# on-403 поведение legacy (settings.scraper_proxy_url
|
||||
# напрямую). Не влияет на use_curl=True (прод-дефолт) —
|
||||
# напрямую). Не влияет на use_curl=True —
|
||||
# там reconnect_on_block=False уже гасит backconnect.
|
||||
config=RealScraperConfig(),
|
||||
origin=serp_origin,
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ async def test_backfill_build_warmed_session_receives_config() -> None:
|
|||
"""#2397 Part D1 / #2330 regression guard: kit's build_warmed_session() now accepts
|
||||
config=ScraperConfig and silently drops settings.scraper_proxy_url (sticky МГТС-прокси)
|
||||
-- NO crash, just a proxy-less session -- unless config=RealScraperConfig() is passed
|
||||
at the call site. use_curl=True is the prod-default path (avito_detail_backfill_use_curl),
|
||||
at the call site. use_curl=True is the curl path (avito_detail_backfill_use_curl),
|
||||
so this is the setup-time build_warmed_session() call (~line 177). assert_awaited_once()
|
||||
alone wouldn't catch someone dropping the kwarg later (mirrors the fetch_detail config=
|
||||
guard above, lines 141-148)."""
|
||||
|
|
|
|||
28
tradein-mvp/backend/tests/test_3045_use_curl_default.py
Normal file
28
tradein-mvp/backend/tests/test_3045_use_curl_default.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
"""#3045: дефолт avito_detail_backfill_use_curl — браузер, а не curl.
|
||||
|
||||
curl_cffi не решает QRATOR PoW Авито (замер 21.08: ~6% обогащений против ~74% на
|
||||
браузере). Пока дефолт был True, браузер включал только environment сервиса scraper,
|
||||
а tradein-backend (ручка админки scrape_avito_detail_backfill исполняет добор в своём
|
||||
процессе) шёл curl'ом. Проверка по значению поля Settings без env и без .env-файла.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
||||
|
||||
import pytest
|
||||
|
||||
from app.core.config import Settings
|
||||
|
||||
|
||||
def test_detail_backfill_defaults_to_browser(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("AVITO_DETAIL_BACKFILL_USE_CURL", raising=False)
|
||||
|
||||
assert Settings(_env_file=None).avito_detail_backfill_use_curl is False
|
||||
|
||||
|
||||
def test_env_still_switches_to_curl(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Контроль: переменная окружения по-прежнему переключает режим — поле живое."""
|
||||
monkeypatch.setenv("AVITO_DETAIL_BACKFILL_USE_CURL", "true")
|
||||
|
||||
assert Settings(_env_file=None).avito_detail_backfill_use_curl is True
|
||||
Loading…
Add table
Reference in a new issue