fix(tradein/browser): сайдкар безусловно резал картинки, а camoufox предупреждал, что за это ловят WAF
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 / browser-tests (pull_request) Successful in 53s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 / browser-tests (pull_request) Successful in 53s
_launch_browser ставил block_images: True для всех провайдеров, и в комментарии стояла только выгода — «рендер быстрее». Цена в комментарии не стояла, хотя camoufox пишет о ней в каждый запуск: LeakWarning: Blocking image requests has been reported to cause detection issues on major WAFs. За три часа прода — 44 таких предупреждения. Авито стоит за QRATOR, доля заглушек в detail-сборе ~32%. Теперь block_images разрешается через _resolve_block_images по образцу соседней _resolve_min_interval: per-provider env -> global env -> код-дефолт. Для avito дефолт — не блокировать. Для cian/yandex/generic дефолт остаётся прежним: их долю блоков никто не мерил, и менять им поведение заодно значило бы поменять три скрапера вслепую. Это не доказанная причина блоков, а единственная названная и документированная — своим же инструментом, в своём же конфиге. Проверять её надо замером на выборке от 40 карточек: блоки идут пачками (#3184), на малых выборках разница неразличима. Итоговая карта пишется в лог на старте, симметрично page-intervals — иначе значение видно только в коде. Refs #3185
This commit is contained in:
parent
e685f96107
commit
f6f494c17b
2 changed files with 103 additions and 3 deletions
|
|
@ -60,6 +60,19 @@ Per-provider модель (#1793):
|
||||||
⚠ BROWSER_BLOCK_RESOURCES (булев выключатель до #1812)
|
⚠ BROWSER_BLOCK_RESOURCES (булев выключатель до #1812)
|
||||||
НЕ читается — см. _RETIRED_ENV; в проде он всё ещё
|
НЕ читается — см. _RETIRED_ENV; в проде он всё ещё
|
||||||
выставлен во всех трёх контейнерах и ни на что не влияет.
|
выставлен во всех трёх контейнерах и ни на что не влияет.
|
||||||
|
BROWSER_BLOCK_IMAGES — булев глобальный дефолт camoufox block_images (#3185).
|
||||||
|
Camoufox сам предупреждает (LeakWarning), что блокировка
|
||||||
|
картинок повышает риск детекта на крупных WAF — картинки
|
||||||
|
часть нормального fingerprint браузера, их отсутствие
|
||||||
|
выделяет сессию. Авито стоит за QRATOR: с block_images=True
|
||||||
|
в проде ~32% detail-сборов возвращали заглушку. Дефолт для
|
||||||
|
avito поэтому False (не блокировать); для cian/yandex/generic
|
||||||
|
остаётся True — их WAF-чувствительность не замеряна, менять
|
||||||
|
без замера не будем.
|
||||||
|
BROWSER_BLOCK_IMAGES_{PROVIDER} — per-provider override, тот же формат, что у
|
||||||
|
BROWSER_MIN_PAGE_INTERVAL_S_{PROVIDER}: PROVIDER в
|
||||||
|
upper-case (AVITO, CIAN, YANDEX, GENERIC), перебивает и
|
||||||
|
global BROWSER_BLOCK_IMAGES, и код-дефолт per-provider.
|
||||||
BROWSER_MIN_PAGE_INTERVAL_S — минимальный интервал (сек) между последовательными
|
BROWSER_MIN_PAGE_INTERVAL_S — минимальный интервал (сек) между последовательными
|
||||||
page.goto ОДНОГО провайдера (default: 2.0). Даёт под-
|
page.goto ОДНОГО провайдера (default: 2.0). Даёт под-
|
||||||
коннектам предыдущей страницы дренироваться, прежде чем
|
коннектам предыдущей страницы дренироваться, прежде чем
|
||||||
|
|
@ -235,6 +248,46 @@ _MIN_PAGE_INTERVAL_BY_PROVIDER: dict[str, float] = {
|
||||||
p: _resolve_min_interval(p) for p in ("avito", "cian", "yandex", "generic")
|
p: _resolve_min_interval(p) for p in ("avito", "cian", "yandex", "generic")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Код-дефолт block_images ПО ПРОВАЙДЕРУ (#3185): avito не измерен пассивно — детект на
|
||||||
|
# QRATOR стоит дороже, чем экономия трафика/времени рендера. cian/yandex/generic не
|
||||||
|
# замерены, поведение не трогаем (True, как раньше).
|
||||||
|
_BLOCK_IMAGES_DEFAULT_BY_PROVIDER: dict[str, bool] = {"avito": False}
|
||||||
|
_BLOCK_IMAGES_DEFAULT_FALLBACK: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_block_images(
|
||||||
|
provider: str,
|
||||||
|
environ: Mapping[str, str] | None = None,
|
||||||
|
) -> bool:
|
||||||
|
"""Возвращает эффективный флаг block_images для провайдера (#3185).
|
||||||
|
|
||||||
|
Приоритет (выше — сильнее): per-provider env ``BROWSER_BLOCK_IMAGES_{PROVIDER}``
|
||||||
|
→ глобальный env ``BROWSER_BLOCK_IMAGES`` → код-дефолт per-provider
|
||||||
|
(``_BLOCK_IMAGES_DEFAULT_BY_PROVIDER``, avito=False, остальные=True). Не кидает
|
||||||
|
исключений — невалидный/отсутствующий env тихо проваливается на следующий уровень.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
provider: имя провайдера ("avito", "cian", "yandex", "generic").
|
||||||
|
environ: env-словарь; None → ``os.environ`` (тестируемость без патча модуля).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True — блокировать image-запросы в camoufox, False — не блокировать.
|
||||||
|
"""
|
||||||
|
env = environ if environ is not None else os.environ
|
||||||
|
provider_default = _BLOCK_IMAGES_DEFAULT_BY_PROVIDER.get(
|
||||||
|
provider, _BLOCK_IMAGES_DEFAULT_FALLBACK
|
||||||
|
)
|
||||||
|
global_value = _parse_bool(env.get("BROWSER_BLOCK_IMAGES"), provider_default)
|
||||||
|
key = f"BROWSER_BLOCK_IMAGES_{provider.upper()}"
|
||||||
|
return _parse_bool(env.get(key), global_value)
|
||||||
|
|
||||||
|
|
||||||
|
# Per-provider эффективные флаги block_images, вычисляются на module-load из os.environ.
|
||||||
|
# Читается в _launch_browser; логируется на старте (_on_startup).
|
||||||
|
_BLOCK_IMAGES_BY_PROVIDER: dict[str, bool] = {
|
||||||
|
p: _resolve_block_images(p) for p in ("avito", "cian", "yandex", "generic")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _provider_for_url(url: str) -> str:
|
def _provider_for_url(url: str) -> str:
|
||||||
"""Определяет поставщика по host URL.
|
"""Определяет поставщика по host URL.
|
||||||
|
|
@ -374,9 +427,14 @@ async def _launch_browser(provider: str, proxy_override: str | None = None) -> N
|
||||||
"locale": "ru-RU",
|
"locale": "ru-RU",
|
||||||
"geoip": True,
|
"geoip": True,
|
||||||
"humanize": True,
|
"humanize": True,
|
||||||
# Скорость: не грузим картинки — нам нужен HTML/JSON, рендер быстрее
|
# block_images НЕ бесплатен (#3185): camoufox сам предупреждает LeakWarning'ом,
|
||||||
# (трафик безлимитный, выигрыш именно по времени загрузки).
|
# что блокировка image-запросов повышает риск детекта на крупных WAF — картинки
|
||||||
"block_images": True,
|
# часть штатного fingerprint браузера, их отсутствие выделяет сессию. Авито за
|
||||||
|
# QRATOR: пока блокировали всем провайдерам одинаково, ~32% detail-сборов
|
||||||
|
# получали заглушку. Теперь значение per-provider (BROWSER_BLOCK_IMAGES /
|
||||||
|
# BROWSER_BLOCK_IMAGES_{PROVIDER}, см. docstring модуля) — avito по дефолту НЕ
|
||||||
|
# блокирует, cian/yandex/generic блокируют как раньше (не измерены, не трогаем).
|
||||||
|
"block_images": _BLOCK_IMAGES_BY_PROVIDER.get(provider, _BLOCK_IMAGES_DEFAULT_FALLBACK),
|
||||||
# Анти-leak: WebRTC может слить реальный IP контейнера мимо прокси → деанон.
|
# Анти-leak: WebRTC может слить реальный IP контейнера мимо прокси → деанон.
|
||||||
"block_webrtc": True,
|
"block_webrtc": True,
|
||||||
# Форсим таймзону +3 МСК. geoip берёт tz одного IP на старте, а БэкКоннект
|
# Форсим таймзону +3 МСК. geoip берёт tz одного IP на старте, а БэкКоннект
|
||||||
|
|
@ -565,6 +623,7 @@ async def _on_startup(app: web.Application) -> None:
|
||||||
list(PROVIDERS),
|
list(PROVIDERS),
|
||||||
)
|
)
|
||||||
logger.info("tradein-browser: page-intervals %s", _MIN_PAGE_INTERVAL_BY_PROVIDER)
|
logger.info("tradein-browser: page-intervals %s", _MIN_PAGE_INTERVAL_BY_PROVIDER)
|
||||||
|
logger.info("tradein-browser: block-images %s", _BLOCK_IMAGES_BY_PROVIDER)
|
||||||
_warn_retired_env()
|
_warn_retired_env()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -490,6 +490,47 @@ def test_pace_provider_independent_per_provider(monkeypatch: pytest.MonkeyPatch)
|
||||||
assert slept == [], "разные провайдеры не пейсят друг друга"
|
assert slept == [], "разные провайдеры не пейсят друг друга"
|
||||||
|
|
||||||
|
|
||||||
|
# ── #3185: _resolve_block_images (per-provider block_images) ───────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_block_images_default_avito_unblocked() -> None:
|
||||||
|
"""Код-дефолт: avito НЕ блокирует картинки (QRATOR-детект, ~32% заглушек)."""
|
||||||
|
assert server._resolve_block_images("avito", environ={}) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_block_images_default_others_blocked() -> None:
|
||||||
|
"""Код-дефолт: cian/yandex/generic блокируют как раньше (не измерены)."""
|
||||||
|
assert server._resolve_block_images("cian", environ={}) is True
|
||||||
|
assert server._resolve_block_images("yandex", environ={}) is True
|
||||||
|
assert server._resolve_block_images("generic", environ={}) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_block_images_global_env_overrides_default_both_ways() -> None:
|
||||||
|
"""Глобальный BROWSER_BLOCK_IMAGES перебивает код-дефолт для ЛЮБОГО провайдера."""
|
||||||
|
assert server._resolve_block_images("avito", environ={"BROWSER_BLOCK_IMAGES": "true"}) is True
|
||||||
|
assert server._resolve_block_images("cian", environ={"BROWSER_BLOCK_IMAGES": "false"}) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_block_images_per_provider_env_overrides_global() -> None:
|
||||||
|
"""Per-provider BROWSER_BLOCK_IMAGES_{PROVIDER} сильнее и global env, и дефолта."""
|
||||||
|
env = {"BROWSER_BLOCK_IMAGES": "false", "BROWSER_BLOCK_IMAGES_CIAN": "true"}
|
||||||
|
assert server._resolve_block_images("cian", environ=env) is True
|
||||||
|
# avito не упомянут per-provider'ом → берёт global (false).
|
||||||
|
assert server._resolve_block_images("avito", environ=env) is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_block_images_per_provider_env_overrides_avito_default() -> None:
|
||||||
|
"""BROWSER_BLOCK_IMAGES_AVITO=true может включить блокировку и для avito."""
|
||||||
|
env = {"BROWSER_BLOCK_IMAGES_AVITO": "true"}
|
||||||
|
assert server._resolve_block_images("avito", environ=env) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_resolve_block_images_unrecognized_value_is_falsy() -> None:
|
||||||
|
"""Нераспознанная строка per-provider env — как везде в _parse_bool: не True."""
|
||||||
|
env = {"BROWSER_BLOCK_IMAGES_CIAN": "not-a-bool"}
|
||||||
|
assert server._resolve_block_images("cian", environ=env) is False
|
||||||
|
|
||||||
|
|
||||||
# ── #2674: отставные переменные окружения ────────────────────────────────────
|
# ── #2674: отставные переменные окружения ────────────────────────────────────
|
||||||
# BROWSER_BLOCK_RESOURCES стоит =true во всех трёх прод-контейнерах, а код перестал
|
# BROWSER_BLOCK_RESOURCES стоит =true во всех трёх прод-контейнерах, а код перестал
|
||||||
# её читать в #1812 (булев выключатель → BROWSER_BLOCK_RESOURCE_TYPES). Блокировка при
|
# её читать в #1812 (булев выключатель → BROWSER_BLOCK_RESOURCE_TYPES). Блокировка при
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue