# Maru hledá byt Projekt pro hledání bytů v Praze. Scrapuje inzeráty ze 7 realitních portálů, filtruje, deduplikuje a generuje interaktivní mapu. **Jazyk komunikace:** Čeština (uživatelka Marie). Kód a komentáře v kódu jsou mix CZ/EN. ## Architektura ``` run_all.sh (orchestrátor) ├─ scrape_and_map.py → byty_sreality.json (Sreality API) ├─ scrape_bezrealitky.py → byty_bezrealitky.json (HTML Apollo cache) ├─ scrape_idnes.py → byty_idnes.json (HTML regex) ├─ scrape_psn.py } → byty_psn.json (React API + curl) ├─ scrape_cityhome.py } → byty_cityhome.json (HTML tabulky) ├─ scrape_bazos.py → byty_bazos.json (HTML regex) └─ scrape_realingo.py → byty_realingo.json (Next.js __NEXT_DATA__) ↓ merge_and_map.py ├─ byty_merged.json (deduplikovaná data) └─ mapa_bytu.html (Leaflet.js mapa) ↓ generate_status.py → status.json + scraper_history.json ↓ server.py (port 8080) → servíruje mapu + status page + ratings API ``` ## Filtry (společné všem scraperům) | Parametr | Hodnota | Poznámka | |----------|---------|----------| | Max cena | 13.5M Kč (Sreality/Realingo/Bezrealitky/iDNES), 14M Kč (PSN/CityHome/Bazoš) | Rozdíl je záměrný | | Min plocha | 69 m² | | | Min patro | 2. NP | 2. NP se na mapě označí varováním | | Dispozice | 3+kk, 3+1, 4+kk, 4+1, 5+kk, 5+1, 6+ | | | Region | Praha | | | Vyloučit | panelové domy, sídliště | regex v popisu/polích | ## Klíčové soubory - **scrape_and_map.py** — Sreality scraper + `generate_map()` funkce (sdílená, generuje HTML mapu) - **merge_and_map.py** — sloučí 7 JSON zdrojů, deduplikuje (klíč: ulice + cena + plocha), volá `generate_map()` - **scraper_stats.py** — utility: `validate_listing()` (validace povinných polí + GPS bounds) a `write_stats()` - **generate_status.py** — generuje status.json a scraper_history.json z výstupů scraperů - **server.py** — HTTP server (port 8080), endpointy: `/mapa_bytu.html`, `/scrapers-status`, `/api/ratings`, `/api/status` - **run_all.sh** — orchestrátor, spouští scrapery postupně (PSN+CityHome paralelně), pak merge + status ## Mapa (mapa_bytu.html) - Leaflet.js + CARTO tiles - Barvy markerů podle ceny/m² (modrá < 110k → červená > 165k, šedá = neuvedeno) - PSN/CityHome = srdíčkové markery (❤️) - Nové inzeráty (≤ 1 den) = žlutý badge "NEW" - Zamítnuté = zprůhledněné + 🚫 SVG overlay - Oblíbené = hvězdička (⭐) - Filtry: patro, max cena (input, default 13.5M, max 14M), datum přidání, skrýt zamítnuté, klik na cenový pás - Ratings uložené v localStorage + sync na server `/api/ratings` ## Barvy zdrojů na mapě ```python source_colors = { "sreality": "#1976D2", # modrá "realingo": "#00897B", # teal "bezrealitky": "#E91E63", # růžová "idnes": "#FF6F00", # oranžová "psn": "#D32F2F", # červená "cityhome": "#D32F2F", # červená "bazos": "#7B1FA2", # fialová } ``` ## Deduplikace (merge_and_map.py) - Klíč: `normalize_street(locality) + price + area` - Normalizace ulice: první část před čárkou, lowercase, odstranění diakritiky, jen alfanumerické znaky - PSN a CityHome mají prioritu (načtou se první) ## Vývoj - **Git remote:** `https://gitea.home.hrajfrisbee.cz/littlemeat/maru-hleda-byt.git` - **Gitea API token:** uložen v `.claude/settings.local.json` - **Python 3.9+** kompatibilita (`from __future__ import annotations`) - **Žádné pip závislosti** — jen stdlib (urllib, json, re, logging, pathlib, subprocess) - **Docker:** `build/Dockerfile` (python:3.13-alpine), cron každé 4 hodiny - Generované soubory (`byty_*.json`, `mapa_bytu.html`, `*.log`) jsou v `.gitignore` ## Typické úlohy ```bash # Rychlý test scraperu python3 scrape_bazos.py --max-pages 1 --max-properties 5 --log-level DEBUG # Lokální validace (všechny scrapery s limity) make validation-local # Vygenerovat mapu z existujících dat python3 merge_and_map.py # Spustit server python3 server.py # nebo: make serve # Plný scrape ./run_all.sh ``` ## Pořadí scraperů v run_all.sh 1. Sreality 2. Bezrealitky 3. iDNES 4. PSN + CityHome (paralelně) 5. Bazoš 6. Realingo (poslední — uživatelka ho nemá ráda) 7. Merge + mapa 8. Status generování ## Konvence - Commit messages v angličtině, PR popis v angličtině - Co-Authored-By: Claude Opus 4.6 - PRy přes Gitea API (viz create_pr.sh pattern v historii) - Nové scrapery kopírují vzor z `scrape_bezrealitky.py` - Každý scraper má argparse s `--max-pages`, `--max-properties`, `--log-level`