feat(tradein): cian_newbuilding.py — ЖК catalog scraper (chart + reliability + offers, Wave 4 Worker C) #459
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#459
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/tradein-cian-newbuilding-parser"
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
Stage 6 / Wave 4 of CianScraper v1. Cian.ru newbuilding (ЖК) catalog page scraper — extracts newbuilding metadata + 6 sister state containers (realtyValuation chart / reliability / reviews / offers / builders / housesByTurn).
🎯 Main Stage 6 deliverable: 7-month price dynamics chart →
houses_price_dynamicstable (Cross-source benchmark для Stage 9 estimator).Files
app/services/scrapers/cian_newbuilding.py(NEW) —fetch_newbuilding()+save_newbuilding_enrichment()+NewbuildingEnrichmentdataclasstests/test_cian_newbuilding.py(NEW) — 20 testsState extraction
https://zhk-<slug>-<city>-i.cian.ru/newbuilding-card-desktop-fichering-frontendinitialState(sister containers live as top-level keys, не separate push entries)Sister containers parsed
realtyValuationdata.priceDynamics.chart.data.{labels[], values[]}+ fallback flatchart[]houses_price_dynamics(UPSERT onhouses_price_dynamics_dim_key)reliability{checkStatus, details[]}+ fallbackchecks[]house_reliability_checksreviewsreviews.items[]house_reviewstable из migration 014)offersdata.total.fromDeveloperRooms[].layouts[]+items[]fast pathbuilders/banks/housesByTurnnewbuilding.*jsonb arrayshouses.builders/banks/houses_by_turnPersistence (
save_newbuilding_enrichment)management_companies(ON CONFLICText_source, ext_id, name) → get mc_idUPDATE houses SET cian_internal_house_id, year_built, management_company_id, transport_accessibility_rate, advantages, banks, builders, houses_by_turnINSERT INTO houses_price_dynamics ... ON CONFLICT ON CONSTRAINT houses_price_dynamics_dim_key(post-029 multi-dimension)INSERT INTO house_reliability_checks(no UNIQUE — каждая scrape inserts; см. TBD)Schema verified
houses.cian_internal_house_id,management_company_id,transport_accessibility_rate,advantages/banks/builders/houses_by_turn jsonbmanagement_companies UNIQUE (ext_source, name, ext_id)houses_price_dynamics_dim_key (house_id, source, room_count, prices_type, period, month_date)— exact ON CONFLICT matchTests
Ruff
Clean (fixed N806 uppercase var, E501 line length, B905 zip strict=).
Anti-bot
curl_cffi.AsyncSession(impersonate="chrome120")— consistent with cian.py / cian_detail.py.TBD / known limitations
room_count='all'). Per-room chart требует separate XHR scraper (Stage 7+ scope).house_reliability_checks— no UNIQUE constraint в migration 025 → repeated scrapes INSERT new rows. Каллер должен truncate/delete existing если нужен dedup (schema decision deferred; future migration 030+ может добавить UNIQUE(house_id, source, check_name)).Refs
decisions/CianScraper_v1_Implementation_Plan.mdStage 6decisions/Schema_Cian_SERP_Inventory.mdsec 15-19 (newbuilding state + sister containers)Test plan
Merged via deep-code-reviewer — verdict ✅ APPROVE.
Stage 6 / Wave 4 Worker C — CianScraper v1 newbuilding (ЖК) catalog scraper. Squash-merged as
d28bf4e6.Cross-checks verified
houses_price_dynamics_dim_keyconstraint name (migration 029:159) matchesON CONFLICT ON CONSTRAINTin cian_newbuilding.pyhouses.cian_internal_house_id, management_company_id, transport_accessibility_rate, advantages/banks/builders/houses_by_turnall presentmanagement_companies UNIQUE (ext_source, name, ext_id)matchesON CONFLICT (ext_source, name, ext_id)(PR body wrongly saysext_source, ext_id, name— actual SQL order is correct)house_reliability_checks (house_id, check_status, check_name, details, source, recorded_at)columns match INSERT[]on empty/None/missing dicttest_extract_chart_handles_emptyzip(labels, values, strict=False)CAST(:x AS type)everywhere — no:x::type(CAST trap rule)curl_cffi impersonate="chrome120"consistent with cian.py / cian_detail.pydb.commit()at end → UPDATE houses + INSERTs roll back together on failureMinor notes (non-blocking, follow-up in vault / future PR)
management_companies dedup with NULL ext_id — Postgres
NULL != NULL→UNIQUE (ext_source, name, ext_id)allows duplicates whenext_id IS NULL. Bounded risk (1 dup per ЖК per scrape if Cian omits id). Mitigation idea: coalesce ext_id to hash of name, or partial unique index on(ext_source, name) WHERE ext_id IS NULL.houses_price_dynamics.price_per_sqmsemantic mismatch — primary chart path stores total RUB values (e.g. 14_000_000) into column literally namedprice_per_sqmwithprices_type='price'discriminator. PR #448 backfill set legacy rows toprices_type='priceSqm'/'allTime'/'all'. Stage 9 estimator MUST filter byprices_typeto avoid mixing total/sqm price series. Acceptable trade-off per PR TBD #1.offers items[]fast path (line ~285): returns raw list without dict-only filter orroom_countinjection. Downstream may get heterogeneous items. Low impact (offers consumed by future Stage 9 matching, not Stage 6 chart).save_newbuilding_enrichmentcaller contract —UPDATE houses WHERE id=:hidsilently no-ops if house missing; subsequent FK tohouses_price_dynamics.house_idwould then raise. Caller must guaranteehouse_idexists. Consider explicitRETURNING idcheck orassert cursor.rowcount == 1.house_reliability_checksunbounded growth — no UNIQUE, every scrape inserts. Documented as TBD. Future migration 030+ should addUNIQUE (house_id, source, check_name)or per-scrape truncate.Test gap — no integration test for
save_newbuilding_enrichment(full DB orchestration) and no per-container failure isolation test. Pilot-acceptable.Stage 9 readiness
Chart data flows into
houses_price_dynamicswith proper multi-dim discriminators (source='cian_realty_valuation', room_count='all', prices_type='price', period='halfYear'). Stage 9 estimator must:source IN ('cian_realty_valuation', ...)and explicitprices_typeto avoid mixing scales.Post-merge expectations (per user request — NOT running qa-tester)
deploy-tradein.ymlshould trigger on merge tomain.SELECT COUNT(*) FROM houses_price_dynamics WHERE house_id = ? AND source = 'cian_realty_valuation' AND prices_type = 'price'should return 7 monthly points.Vault update suggestion:
code/modules/tradein/Cian_Newbuilding_Parser.mddocumenting the prices_type='price' vs 'priceSqm' coexistence + management_companies NULL ext_id risk.