"""CLI for ad-hoc naш.дом.рф kn-API sweeps. Usage: PYTHONIOENCODING=utf-8 python data/sql/50_scrape_kn_flats.py --regions 66 python data/sql/50_scrape_kn_flats.py --regions 66 --developers 6208_0 --headed """ from __future__ import annotations import argparse import asyncio import logging import os import sys from pathlib import Path # allow `from app.services.scrapers.domrf_kn import ...` when run from repo root ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "backend")) from app.services.scrapers.domrf_kn import run_region_sweep # noqa: E402 def main() -> None: parser = argparse.ArgumentParser(description="naш.дом.рф kn-API scraper (CLI)") parser.add_argument( "--regions", type=int, nargs="+", required=True, help="Region codes (1..89). e.g. --regions 66 77", ) parser.add_argument( "--developers", type=str, nargs="*", help="Optional developer IDs in '_0' format. e.g. --developers 6208_0", ) parser.add_argument("--headed", action="store_true", help="Show Chromium window during bootstrap") parser.add_argument( "--save-state", type=str, default=None, help="Path to dump Playwright storage_state.json after bootstrap (commit to git for server reuse)", ) parser.add_argument( "--load-state", type=str, default=None, help="Path to load Playwright storage_state.json (server uses fingerprint captured locally)", ) parser.add_argument( "--bootstrap-only", action="store_true", help="Just bootstrap a session and exit — useful for capturing storage_state without scraping", ) parser.add_argument("--verbose", "-v", action="store_true") args = parser.parse_args() logging.basicConfig( level=logging.DEBUG if args.verbose else logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s", ) if not os.environ.get("DATABASE_URL"): print("WARNING: DATABASE_URL not set; using default from app.core.config") async def _run() -> None: if args.bootstrap_only: from app.services.scrapers.stealth import BrowserSession async with BrowserSession( region_code=args.regions[0], headed=args.headed, load_state=args.load_state, save_state=args.save_state, ): print(f"bootstrap-only: state saved to {args.save_state}") return for region in args.regions: summary = await run_region_sweep( region_code=region, developers=args.developers or None, headed=args.headed, load_state=args.load_state, save_state=args.save_state if region == args.regions[0] else None, ) print(f"region={region} -> {summary}") asyncio.run(_run()) if __name__ == "__main__": main()