fix(tradein): system-curl subprocess transport for Yandex SERP over SOCKS5 #940

Merged
bot-reviewer merged 1 commit from feat/yandex-curl-subprocess-transport into main 2026-05-31 22:39:06 +00:00
Owner

Проблема (диагностировано live на проде)

Yandex full-load (#931/#937) задеплоен, но даёт 0 листингов. Причина — НЕ код фичи и НЕ прокси:

  • Прокси рабочий: raw curl --compressed -x socks5h://…bproxy.site:15555 тащит полную SERP-страницу за ~17с (448 КБ gzip, 1018 карточек, «407 объявлений»).
  • Но curl_cffi (HTTP-движок скрейпера) над тем же SOCKS5 коллапсирует до <1 КБ/с (67–270 КБ за 45–110с, не докачивает) — chrome120-импersonation TLS/HTTP2 не дружит с этим socks5-прокси. HTTP-вариант прокси (mproxy.site:15555) Яндекс тарпитит на уровне коннекта.
  • Страница+регион верные (через обычный браузер — 407 студий, без captcha). Cookies (31) грузятся.

Фикс

Транспорт-свитч в yandex_realty._http_get:

  • settings.yandex_proxy_url начинается с socks5:///socks5h:// → новый _curl_subprocess_get (системный curl -sS -L --compressed -x <proxy> -A chrome-UA -b <cookies> -w <status-marker>).
  • иначе (dev / no-proxy / http-proxy) → прежний curl_cffi путь без изменений (нет регресса).
  • _CurlResponse shim даёт .status_code/.text — callers (fetch_around, _fetch_page_html) не тронуты.
  • Status парсится из -w маркера (rfind + срез). curl exit 28 (timeout, частичное тело) → status_code=0 → caller трактует как fail. Cookie/proxy не логируются.

Деплой

Прод-env уже готов (выставлено в ходе диагностики): YANDEX_PROXY_URL=socks5h://…bproxy.site:15555 + cookies на /app/yandex_cookies.json. После мерджа+деплоя yandex-full-load должен реально сохранять листинги. ⚠️ Cookies сейчас залиты docker cp (переживают recreate, но НЕ деплой git reset) — follow-up нужен: persistent volume-mount для cookie-файла (отдельный PR), иначе следующий деплой их сотрёт.

Тесты

  • ruff check + format: All checks passed.
  • pytest -k "yandex": 309 passed. 3 новых теста (socks5→curl-subprocess routing, -w marker parse/strip, http→curl_cffi). 2 существующих теста обновлены под settings-импорт в _http_get.

Системный curl в контейнере есть. Не запускалось против prod в тестах.

## Проблема (диагностировано live на проде) Yandex full-load (#931/#937) задеплоен, но даёт **0 листингов**. Причина — НЕ код фичи и НЕ прокси: - Прокси рабочий: raw `curl --compressed -x socks5h://…bproxy.site:15555` тащит полную SERP-страницу за **~17с** (448 КБ gzip, 1018 карточек, «407 объявлений»). - Но **`curl_cffi`** (HTTP-движок скрейпера) над тем же SOCKS5 коллапсирует до **<1 КБ/с** (67–270 КБ за 45–110с, не докачивает) — chrome120-импersonation TLS/HTTP2 не дружит с этим socks5-прокси. HTTP-вариант прокси (`mproxy.site:15555`) Яндекс тарпитит на уровне коннекта. - Страница+регион верные (через обычный браузер — 407 студий, без captcha). Cookies (31) грузятся. ## Фикс Транспорт-свитч в `yandex_realty._http_get`: - `settings.yandex_proxy_url` начинается с `socks5://`/`socks5h://` → новый `_curl_subprocess_get` (системный `curl -sS -L --compressed -x <proxy> -A chrome-UA -b <cookies> -w <status-marker>`). - иначе (dev / no-proxy / http-proxy) → **прежний curl_cffi путь без изменений** (нет регресса). - `_CurlResponse` shim даёт `.status_code`/`.text` — callers (`fetch_around`, `_fetch_page_html`) не тронуты. - Status парсится из `-w` маркера (`rfind` + срез). curl exit 28 (timeout, частичное тело) → `status_code=0` → caller трактует как fail. Cookie/proxy не логируются. ## Деплой Прод-env уже готов (выставлено в ходе диагностики): `YANDEX_PROXY_URL=socks5h://…bproxy.site:15555` + cookies на `/app/yandex_cookies.json`. После мерджа+деплоя `yandex-full-load` должен реально сохранять листинги. ⚠️ Cookies сейчас залиты `docker cp` (переживают recreate, но НЕ деплой `git reset`) — **follow-up нужен**: persistent volume-mount для cookie-файла (отдельный PR), иначе следующий деплой их сотрёт. ## Тесты - ruff check + format: All checks passed. - `pytest -k "yandex"`: **309 passed**. 3 новых теста (socks5→curl-subprocess routing, `-w` marker parse/strip, http→curl_cffi). 2 существующих теста обновлены под settings-импорт в `_http_get`. Системный `curl` в контейнере есть. Не запускалось против prod в тестах.
lekss361 added 1 commit 2026-05-31 22:36:53 +00:00
lekss361 added the
scope/backend
scrapers
labels 2026-05-31 22:36:59 +00:00
bot-reviewer approved these changes 2026-05-31 22:38:50 +00:00
bot-reviewer left a comment
Collaborator

APPROVE — прагматичный subprocess-транспорт, инъекций нет, curl_cffi-путь сохранён. Мержу.

Verified (security-sensitive — subprocess)

  • Нет command injection: asyncio.create_subprocess_exec(*args) (НЕ _shell) → argv идёт в execve напрямую, без shell-интерпретации. url/proxy/cookie-значения — отдельные argv-элементы → shell-метасимволы безвредны. ✓
  • Routing: proxy.startswith(("socks5://","socks5h://")) → subprocess; иначе legacy curl_cffi без изменений (http-proxy/no-proxy/dev). Тесты покрывают оба + http→cffi. ✓
  • Status parse: -w маркер аппендится последним → rfind корректно извлекает + срезает из body; ValueError→0; curl rc=28 (timeout)→status 0 (частичное tarpit-тело отбрасывается, не сохраняется). ✓
  • _CurlResponse shim (.status_code/.text) — callers (fetch_around/_fetch_page_html) не тронуты. Cross-file = только yandex_realty. 309 passed.
  • Creds: proxy/cookies напрямую не логируются (только url + err[:200]).

🟡 Advisory

  • Proxy-creds видны в argv через ps — single-tenant контейнер, pre-existing паттерн curl+proxy, приемлемо.

🟠 Операционный trap (завожу follow-up — НЕ блокер merge кода)

Автор флагнул: cookies залиты docker cp → НЕ переживают git reset deploy → следующий деплой сотрёт /app/yandex_cookies.json → yandex full-load снова молча вернёт 0. Код merge-safe, но нужен persistent volume-mount для cookie-файла отдельным PR. Завожу issue чтобы не потерять.

Squash-merge + delete branch.

<!-- gendesign-review-bot: sha=4f7426b verdict=approve --> ✅ **APPROVE** — прагматичный subprocess-транспорт, инъекций нет, curl_cffi-путь сохранён. Мержу. ## Verified (security-sensitive — subprocess) - **Нет command injection**: `asyncio.create_subprocess_exec(*args)` (НЕ `_shell`) → argv идёт в execve напрямую, без shell-интерпретации. `url`/`proxy`/cookie-значения — отдельные argv-элементы → shell-метасимволы безвредны. ✓ - **Routing**: `proxy.startswith(("socks5://","socks5h://"))` → subprocess; иначе legacy curl_cffi без изменений (http-proxy/no-proxy/dev). Тесты покрывают оба + http→cffi. ✓ - **Status parse**: `-w` маркер аппендится последним → `rfind` корректно извлекает + срезает из body; `ValueError`→0; curl rc=28 (timeout)→status 0 (частичное tarpit-тело отбрасывается, не сохраняется). ✓ - `_CurlResponse` shim (`.status_code`/`.text`) — callers (`fetch_around`/`_fetch_page_html`) не тронуты. Cross-file = только yandex_realty. 309 passed. - Creds: proxy/cookies напрямую не логируются (только `url` + `err[:200]`). ## 🟡 Advisory - Proxy-creds видны в argv через `ps` — single-tenant контейнер, pre-existing паттерн curl+proxy, приемлемо. ## 🟠 Операционный trap (завожу follow-up — НЕ блокер merge кода) Автор флагнул: cookies залиты `docker cp` → НЕ переживают `git reset` deploy → следующий деплой сотрёт `/app/yandex_cookies.json` → yandex full-load снова молча вернёт 0. Код merge-safe, но нужен persistent volume-mount для cookie-файла отдельным PR. Завожу issue чтобы не потерять. Squash-merge + delete branch.
bot-reviewer merged commit a23e650fae into main 2026-05-31 22:39:06 +00:00
bot-reviewer deleted branch feat/yandex-curl-subprocess-transport 2026-05-31 22:39:07 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#940
No description provided.