Авито: добор карточек больше не сжигает 1600 попыток на упавшем сайдкаре и везде ходит браузером #3560
4 changed files with 53 additions and 13 deletions
|
|
@ -1194,14 +1194,6 @@ class Settings(BaseSettings):
|
|||
# False = старое поведение (без фильтра). ENV: AVITO_SERP_EKB_ONLY.
|
||||
avito_serp_ekb_only: bool = Field(default=True, validation_alias="AVITO_SERP_EKB_ONLY")
|
||||
|
||||
# ── Yandex SERP cookies (#801/T4) ───────────────────────────────────────
|
||||
# Путь к JSON-файлу с cookies браузера (формат: [{name, value, ...}, ...]).
|
||||
# Если задан и файл существует — cookies передаются в curl_cffi-сессию при
|
||||
# Yandex SERP-запросах; снижает вероятность captcha на datacenter IP.
|
||||
# Пусто / файл не найден = запросы без cookies (не падаем, только warning).
|
||||
# ENV: YANDEX_COOKIES_FILE.
|
||||
yandex_cookies_file: str | None = None
|
||||
|
||||
# ── #639: Cian browser auto-login (Variant B) ────────────────────────────
|
||||
# Провалидировано вживую 2026-05-31: email+пароль, без SMS/капчи. Флоу 2-шаговый
|
||||
# (после 1-го сабмита экран «Введите пароль» → повтор). Селекторы env-overridable.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
"""#3182: режим detail-добора Авито не зависит от того, какой контейнер взял задачу.
|
||||
|
||||
Добор исполняют два процесса образа backend: планировщик (tradein-scraper) и ручка
|
||||
админки scrape_avito_detail_backfill (tradein-backend). Хостовый backend/.env.runtime
|
||||
задаёт AVITO_DETAIL_BACKFILL_USE_CURL=true, а environment сервиса перекрывает env_file —
|
||||
до правки "false" стояло только у scraper, и printenv на проде давал scraper=false,
|
||||
backend=true. Проверка по значению: разобранный compose, каждый сервис образа backend.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from app.core.config import Settings
|
||||
|
||||
_COMPOSE = Path(__file__).resolve().parents[2] / "docker-compose.prod.yml"
|
||||
|
||||
|
||||
def test_every_backend_image_service_pins_browser_transport() -> None:
|
||||
services = yaml.safe_load(_COMPOSE.read_text())["services"]
|
||||
backend_image = {
|
||||
name: svc
|
||||
for name, svc in services.items()
|
||||
if "gendesign-tradein-backend" in svc.get("image", "")
|
||||
}
|
||||
# Контроль разбора: без него пустой словарь дал бы зелёный тест ни о чём.
|
||||
assert {"backend", "scraper"} <= set(backend_image), sorted(backend_image)
|
||||
|
||||
values = {
|
||||
name: (svc.get("environment") or {}).get("AVITO_DETAIL_BACKFILL_USE_CURL")
|
||||
for name, svc in backend_image.items()
|
||||
}
|
||||
assert values == dict.fromkeys(backend_image, "false"), values
|
||||
|
||||
|
||||
def test_leftover_yandex_cookies_env_is_ignored(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""YANDEX_COOKIES_FILE удалён из Settings (никто не читал); хостовый .env.runtime
|
||||
его ещё несёт — старт не должен падать, а поле не должно всплыть обратно."""
|
||||
monkeypatch.setenv("YANDEX_COOKIES_FILE", "/app/yandex_cookies.json")
|
||||
|
||||
assert not hasattr(Settings(_env_file=None), "yandex_cookies_file")
|
||||
|
|
@ -40,10 +40,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|||
import pytest
|
||||
|
||||
|
||||
def _mock_settings(
|
||||
scraper_proxy_url: str | None,
|
||||
yandex_cookies_file: str | None = None,
|
||||
) -> SimpleNamespace:
|
||||
def _mock_settings(scraper_proxy_url: str | None) -> SimpleNamespace:
|
||||
"""Minimal settings stand-in with fields read by proxy helpers and scraper __aenter__."""
|
||||
return SimpleNamespace(
|
||||
scraper_proxy_url=scraper_proxy_url,
|
||||
|
|
@ -52,7 +49,6 @@ def _mock_settings(
|
|||
# removed — dead mobileproxy accounts). Mirrored here for test compat.
|
||||
cian_proxy_url=scraper_proxy_url,
|
||||
yandex_proxy_url=scraper_proxy_url,
|
||||
yandex_cookies_file=yandex_cookies_file,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -340,6 +340,11 @@ services:
|
|||
# деградация. У scraper те же значения с #2126/#2160.
|
||||
USE_PROXY_POOL_CURL: "true"
|
||||
USE_PROXY_POOL_BROWSER: "true"
|
||||
# #3182: ручка админки scrape_avito_detail_backfill исполняет добор В ЭТОМ
|
||||
# процессе. Хостовый backend/.env.runtime задаёт true (curl не проходит QRATOR
|
||||
# PoW, см. scraper ниже) — environment перекрывает env_file. Значение одно на
|
||||
# все сервисы образа backend, чтобы режим не зависел от того, кто взял задачу.
|
||||
AVITO_DETAIL_BACKFILL_USE_CURL: "false"
|
||||
depends_on:
|
||||
browser:
|
||||
condition: service_started
|
||||
|
|
@ -453,6 +458,8 @@ services:
|
|||
# Settings()-объект образа где-то читает флаг при импорте (defense-in-depth,
|
||||
# аналогично backend). tgbot_main.py не должен зависеть от этого значения.
|
||||
SCHEDULER_ENABLE: "false"
|
||||
# #3182: то же значение, что у backend/scraper (см. комментарий у backend).
|
||||
AVITO_DETAIL_BACKFILL_USE_CURL: "false"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue