fix(yandex-valuation): curl_cffi chrome120 — Yandex gates SSR on Chrome TLS #545
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#545
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-yandex-curl-cffi"
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?
Summary
YandexValuationScraperoverrides__aenter__/__aexit__/_http_getto usecurl_cffi.requests.AsyncSession(impersonate='chrome120')BaseScraperuntouched (other scrapers don't need Chrome TLS gate)yandex_realty.py+scripts/local-sweep-ekb-yandex.pyWhy (live evidence)
Probe today: plain
httpx/curlGET to/otsenka-kvartiry-po-adresu-onlayn/returns 200 OK with 1.27 MB HTML — but body contains ONLY the table header row (__head). Data rows are client-side rendered. Yandex anti-bot gates SSR on Chrome TLS+JA3 fingerprint.With curl_cffi
chrome120: same URL → 21__rowoccurrences, 133__cell, 23 м², 38 dates — full SSR data.This explains why the DOM-based parser from PR #544 returned 0 rows on re-sweep: rows simply weren't in the HTML the scraper received.
Tests
curl_cffi==0.15.0already in deps (nopyproject.toml/uv.lockchanges)Followup
After merge: purge yandex_valuation history+cache, re-sweep Базовый 52 — expected
plausible area_filled ≥90%with DOM parser now having actual DOM nodes to parse.Deep Code Review — PR #545 (yandex_valuation curl_cffi chrome120)
Status: APPROVE
Files: 1 (P1 scraper) | Lines: +26 / -0 | SHA verified:
ed6e52fmatches head.Verdict summary
Targeted override of
__aenter__/__aexit__/_http_getonYandexValuationScraperto usecurl_cffi.requests.AsyncSession(impersonate="chrome120"). BaseScraper untouched — zero blast-radius on other scrapers (Avito, Cian, Yandex realty/detail/newbuilding). Live evidence in PR description is convincing: plain-httpx 1.27 MB HTML contains only table header (__head), curl_cffi same URL returns 21__row× 133__cell. This is the upstream cause of the #544 DOM parser returning 0 rows.Verified contracts
curl-cffi>=0.7.0already intradein-mvp/backend/pyproject.toml(no new dep, no lockfile churn).fetch_house_historyuses only.status_code+.text— both present on curl_cffiResponse. ✓AsyncSession.close()is a coroutine (verified against curl-cffi docs) —await self._cffi_session.close()correct. Session created bare withoutasync withis supported by curl-cffi.admin.py:915,estimator.py:397) wrap inasync with YandexValuationScraper() as scraper:— theRuntimeErrorguard in_http_getwill not fire in practice.kwargs.setdefault("timeout", 30)— explicit, slightly more lenient than BaseScraper's 20 s, reasonable for SSR Yandex.verify=False— curl_cffi default verification preserved.Acknowledged trade-offs (documented in PR)
_http_gethas@retry(stop_after_attempt(3)). Override does NOT inherit it (decorator drops on method override). PR docstring states this explicitly. Caller pathfetch_house_historyalready wraps intry/except Exception → return None, so a transient TLS/network blip produces the same outcome as before (logged exception, no rows for that page). Acceptable; same blast radius as pre-PR error path.super().__aenter__()not called — httpxself._clientstaysNone. Intentional (Yandex valuation never uses httpx). No inherited helper currently dereferencesself._client. Future-proof note only.Tests
PR description correct: all 40 existing tests in
test_yandex_valuation.py/test_yandex_valuation_save.pyare pure-parser (operate on raw HTML strings, never touch_http_getor session). No mocks needed; nothing to update.Minor (non-blocking)
yandex_realty.py" —yandex_realty.pyactually still uses BaseScraper httpx (no curl_cffi import). True sibling isscripts/local-sweep-ekb-yandex.py. Wording quibble only.CurlCffiSessionMixininstead of duplicating the 3-method override.Cross-file impact
admin.py:905—POST /scrape/yandex-valuationasync withestimator.py:397—_get_or_fetch_yandex_valuation_cachedasync withVault cross-check
Consistent with PR history #538 (area sanity cap), #541 (date-order flip), #544 (DOM parser rewrite). This PR completes the loop — DOM parser from #544 was correct, it just had no DOM to parse because TLS was the gate. After merge: PR followup says re-sweep Базовый 52 to verify
area_filled ≥90%.Pre-flight
--no-verify/--amend/--forceevidence.fix/tradein-yandex-curl-cffi.Approving — merge unblocks #544 DOM parser working in production.