-- 204_cian_oblast_sweeps_secondary.sql -- Включить сбор вторички Циана по 4 областным city-sweep'ам (Свердловская обл., -- миграция 179 — nizhniy_tagil/kamensk_uralskiy/pervouralsk/serov). -- -- ПРОБЛЕМА: _job_cian_city_sweep (scraper_kit.orchestration.scheduler:570) читает -- newbuilding_only = bool(default_params.get("newbuilding_only", True)) — дефолт True. -- run_cian_city_sweep (pipeline.py:2436) фильтрует SERP-результат на -- listing_segment == "novostroyki" ДО save_listings, вторичку отбрасывает -- (counters.lots_dropped_secondary). -- -- Дефолт осмыслен для ЕКБ: docstring run_cian_city_sweep прямо говорит, что -- вторичку авторитетно собирает run_cian_full_load (exhaustive региональный сбор). -- НО run_cian_full_load (pipeline.py:2790) хардкодит city=EKATERINBURG_CITY_NAME — -- параметра города там нет вообще, область не покрывает. Итог: областную вторичку -- Циана не собирает НИКТО (городская развёртка её выбрасывает, full_load туда не -- ходит) — областные schedule'ы склонированы с ЕКБ (миграция 179) и унаследовали -- предположение, которое для них неверно. -- -- Прод-счётчики (scrape_runs.counters, последние runs на 2026-08-02) подтверждают: -- pervouralsk 55 увидено, 53 выброшено (сохранено 2) -- kamensk_uralskiy 113 увидено, 108 выброшено (сохранено 5) -- nizhniy_tagil 184 увидено, 176 выброшено (сохранено 3) -- verkhnyaya_pyshma 38 увидено, 16 выброшено (сохранено 9) -- см. EXCLUSION ниже -- -- FIX: newbuilding_only: false для ЧЕТЫРЁХ областных source'ов. cian_city_sweep (ЕКБ, -- БЕЗ суффикса города) НЕ трогаем — для него дефолт корректен (вторичку ЕКБ -- собирает cian_full_load), включение дало бы дублирующую нагрузку на источник. -- -- !!! EXCLUSION: cian_city_sweep_verkhnyaya_pyshma НЕ включён в эту миграцию !!! -- Верхняя Пышма физически ~15 км от центра Екатеринбурга — geo-проверка по -- ST_DWithin (координаты listings vs центр города) показала, что 5 из 22 (23%) -- текущих cian-строк с меткой city="Верхняя Пышма" физически лежат в 15 км от -- центра ЕКБ, т.е. это загрязнённая городская разметка (sweep по anchor'у В.Пышмы -- зацепляет краевые екатеринбургские объявления и подписывает их не тем городом). -- Колонка listings.city — money-critical: её читает asking_to_sold_ratio.py -- (city-скоуп ASKING vs SOLD стороны, #2583 H2) — неверная метка двигает выкупные -- цены. При newbuilding_only=false объём cian-строк под меткой В.Пышма вырастет с -- 22 до нескольких сотен (те же ~38 увидено/16 выброшено за один run, помноженные -- на число прогонов) — 23%-загрязнение умножилось бы пропорционально. -- nizhniy_tagil/kamensk_uralskiy/pervouralsk/serov — загрязнение по той же -- geo-проверке НУЛЕВОЕ (0 из 8/5/2 соответственно физически в ЕКБ) — включать -- безопасно. cian_city_sweep_verkhnyaya_pyshma будет включён ОТДЕЛЬНОЙ миграцией -- после починки городской разметки sweep'а (правится параллельно) — НЕ забыт. -- -- Нагрузка на источник (см. PR description / vault fix-запись для полного разбора): -- fetch_around_multi_room (providers/cian/serp.py:209) НЕ принимает newbuilding_only/ -- secondary_only — SERP-фаза (все rooms×pages) выполняется ОДИНАКОВО независимо от -- этого флага. Фильтр в pipeline.py:2436 применяется ПОСЛЕ фетча, ДО save — чисто -- in-memory отсечение уже оплаченных запросов. HTTP-нагрузка на cian.ru НЕ меняется; -- меняется только объём save_listings (DB-writes) — на порядок больше СОХРАНЯЕМЫХ -- строк, не больше запросов к источнику. detail_top_n=10 detail-фетчей тоже не растёт -- (LIMIT :lim константен, лишь конкурирующий пул кандидатов расширяется). -- -- Дубли: run_cian_full_load всегда region_code=EKB (city_region_id=4743 через -- CianScraper() без city_slug), областные sweeps используют CITY_LOCATIONS[] -- .cian_region_id (4886/4781/4925/4982 — все != 4743) — SERP-запросы физически -- разных региональных выдач. dedup_hash = sha256(source|source_id) — глобальный -- Cian offer_id, ON CONFLICT (dedup_hash) DO UPDATE — даже в теоретическом edge-case -- совпадения upsert НЕ создаёт дубль-строку. -- -- listing_segment: providers/cian/serp.py:892 — вторичка получает -- listing_segment = "vtorichka" (НЕ NULL) → проходит фильтр -- "listing_segment IS NULL OR listing_segment = 'vtorichka'" в asking_to_sold_ratio.py -- и buildings_query.py — новые лоты попадут в оценку без доп. кода. -- -- Мердж jsonb (COALESCE || ...), НЕ перезапись — сохраняет city/radius_m/detail_top_n/ -- enrich_houses/pages_per_anchor/request_delay_sec (см. 179_scrape_schedules_seed_oblast_city_sweeps.sql -- за текущими прод-значениями). Idempotent: повторный прогон ставит то же значение. -- -- ЗАВИСИМОСТИ: 052_scrape_schedules.sql (таблица), 179 (seed этих source'ов). -- deploy order: только миграция — код scheduler.py/pipeline.py НЕ меняется в этом PR, -- дефолт newbuilding_only=True в коде остаётся (правильный fallback для будущих -- source'ов без явного default_params override). BEGIN; UPDATE scrape_schedules SET default_params = COALESCE(default_params, '{}'::jsonb) || '{"newbuilding_only": false}'::jsonb WHERE source IN ( 'cian_city_sweep_nizhniy_tagil', 'cian_city_sweep_kamensk_uralskiy', 'cian_city_sweep_pervouralsk', 'cian_city_sweep_serov' ); COMMIT;