Lou·Ka
Tous les logements à louer du Québec. Un seul endroit.
www.lou-ka.com — un service Groupe Ka

Description
Chercher un appartement au Québec, c'est ouvrir des dizaines de sites web différents — chacun avec sa navigation, ses filtres, son format. Lou-Ka retourne le problème : un connecteur dédié par gestionnaire immobilier visite chaque site, normalise chaque annonce vers un schéma unique, et détecte les changements en continu.
Lou-Ka n'est pas une plateforme d'annonces : c'est un index fidèle et un agrégateur indépendant de logements locatifs. Aucun prix inventé, aucune coordonnée devinée, et chaque fiche renvoie vers l'annonce originale du gestionnaire via une passerelle de sortie transparente (/passerelle/{uid}).
Les sites d'agences n'offrent pas de webhooks. Lou-Ka reproduit l'équivalent : synchronisation périodique + hash de contenu → ajouts, mises à jour et retraits détectés automatiquement. Une annonce qui disparaît du site source disparaît de Lou-Ka (et répond
410 Goneaux moteurs de recherche).
En chiffres : 22 900+ annonces actives, 275 sources recensées, 212 connecteurs, 820 villes, 11 régions, ~70 % des annonces géolocalisées.
Fonctionnalités
- Recherche filtrée — ville, quartier, type (3½, 4½…), loyer min/max, animaux, meublé, superficie, texte libre.
- Vue carte 3D — Lou-Ka Maps (framework
@groupe-ka/ka-maps, moteur Mapbox GL JS) : clusters par prix moyen, « Rechercher dans cette zone », caméra partageable dans l'URL, bascule 3D/2D, synchro liste ↔ carte. - Fiches complètes — toutes les photos, digest structuré de la description (text mining regex FR, aucun LLM), badge marché, historique de prix (
price_log), bloc « Le quartier » (recensement 2021, INSPQ, SPVM, écoles MEQ, POI OSM), fiche PDF avec QR code. - Pages villes SEO —
/villes,/ville/{slug},/ville/{slug}/{type}avec SSR léger, sitemaps dynamiques (chunks de 10 000), JSON-LD (RealEstateListing+OfferCAD), vrais404/410. - Observatoire du marché — KPI temps réel, loyers médians par région/ville/taille, rapport de marché PDF multi-pages (
/api/stats/rapport.pdf). - Registre des sources — chaque gestionnaire, son statut, sa dernière synchro ; toute source non connectable est documentée avec sa raison dans
data/sources.json. - Comptes & SSO KA — « Se connecter avec KA » via le hub groupe-ka.com (JWT HS256, audience
lou-ka,ka_idémis par le hub) + Google OAuth direct ; favoris synchronisés vers « Mon univers Ka » ; pages gestionnaires réclamables (/g/{source_id}) ; profil public opt-in/u/{ka_id}. - Widget KA Agent — bulle de chat IA du Groupe Ka intégrée au site.
- PWA mobile-first installable + app iOS SwiftUI 100 % native (iOS 17+, TestFlight, dépôt séparé
git.spboucher.ai/lou-ka-ios) avec moteur de recommandation on-device. - Robustesse — garde-fou anti-dérive (
DRIFT_RATIO = 0.25) : si une source retourne soudainement beaucoup moins d'annonces, les retraits sont suspendus au lieu de vider l'inventaire.
Visite guidée
| Recherche filtrée | Vue carte Lou-Ka Maps |
![]() |
![]() |
| Fiche complète | Pages villes (SEO) |
![]() |
![]() |
| Observatoire du marché | Registre des sources |
![]() |
![]() |
Stack technique
| Couche | Technologies | Fichiers |
|---|---|---|
| Backend | Python 3.14, FastAPI, uvicorn, SQLite (migrations auto, upsert par hash), reportlab/fpdf2 (PDF) — 8 dépendances | louka/ |
| Connecteurs | 205 modules auto-découverts (pkgutil.iter_modules) : HTML, API JSON internes (Building Stack, RealVuu, Planpoint, Rentsync, source.immo, WordPress REST…), Firecrawl (Cloudflare), Scrapfly (anti-bot) |
louka/connectors/*.py |
| Text mining | digest déterministe des descriptions — regex FR, < 5 ms/annonce, aucun LLM | louka/textmine.py |
| Géocodage | Nominatim (1 req/s) + Adresses Québec (MERN ArcGIS, mode EN LOT de 200) validé par bounding box provinciale | louka/geocode.py |
| Frontend | React 18 + Vite + TypeScript, design « éditorial sharp » Groupe Ka (Space Grotesk, accent), PWA | frontend/ |
| Cartographie | @groupe-ka/ka-maps — framework partagé du Groupe Ka, Mapbox GL JS style Standard 3D, monté en React.lazy |
frontend/src/kamaps/ |
| SEO | SSR léger FastAPI, sitemaps dynamiques, JSON-LD, hreflang, GZip, /assets immuable 1 an |
louka/seo.py |
| iOS | SwiftUI natif (iOS 17+, zéro dépendance externe), TestFlight | dépôt séparé |
| Prod | PM2 (3 processus) + ngrok sur le nœud M3U96b | — |
Structure du dépôt
lou-ka/
├── run.py # point d'entrée CLI (sync / watch / serve / geocode / poi / quartier / record)
├── requirements.txt # 8 dépendances backend (FastAPI, uvicorn, bs4, reportlab, fpdf2…)
├── louka/ # paquet backend (Python 3.14)
│ ├── web.py # app FastAPI : API JSON + service du build Vite
│ ├── seo.py # SSR léger, pages programmatiques, sitemaps, robots, JSON-LD
│ ├── db.py # SQLite : migrations auto, upsert par hash, anti-dérive
│ ├── schema.py # dataclass Listing
│ ├── normalize.py # normalisation (prix, dates ISO, types d'unités, adresses)
│ ├── textmine.py # digest déterministe des descriptions (regex FR)
│ ├── ingest.py # pipeline sync / watch
│ ├── auth.py # SSO KA + Google OAuth, sessions HMAC-SHA256 (cookie httpOnly 30 j)
│ ├── accounts.py # rôles, favoris, réclamation de page gestionnaire
│ ├── profile.py / hubprofile.py / hubfav.py # profils & intégration hub Groupe Ka
│ ├── geocode.py # Nominatim + Adresses Québec (mode EN LOT)
│ ├── poi.py / quartier.py # commodités OSM, stats de quartier
│ ├── marketstats.py # agrégats du marché (source unique stats/API/PDF)
│ ├── pdfgen.py # fiches PDF + rapport de marché (QR code)
│ ├── fixtures.py # enregistrement/rejeu HTTP pour tests hors-ligne
│ └── connectors/ # 212 connecteurs + base.py (auto-découverte)
├── frontend/ # React 18 + Vite + TypeScript (pages, composants, kamaps/)
├── data/
│ ├── sources.json # registre des 265 sources (statut + raison si non connectable)
│ ├── louka.db # base de production (gitignorée)
│ └── quartier.db # base statique de quartier (gitignorée)
├── scripts/ # build_recensement / build_contexte / build_environnement / merge_quartier / screenshots.mjs
├── tests/ # pytest + fixtures HTTP rejouables hors-ligne
├── reports/ # audits : fiches connecteurs, expansion par région, quartier…
└── docs/ # screenshot.png, screenshots/, ka-maps-lou-ka.mdDémarrage local
git clone https://git.spboucher.ai/lou-ka.git && cd lou-ka
# Backend
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# Frontend (nécessite le framework ka-maps bâti à côté : ../ka-maps)
cd frontend && npm install && npm run build && cd ..
# (optionnel) sites JavaScript / anti-bot
cat > .env <<EOF
FIRECRAWL_API_KEY=fc-votre-cle
SCRAPFLY_KEY=votre-cle
EOF
# Ingestion puis service
.venv/bin/python run.py sync # toutes les sources (ou : run.py sync logisco msi)
.venv/bin/python run.py serve 8080 # → http://localhost:8080
.venv/bin/python run.py watch 60 # resynchronisation en boucle (minutes)En développement frontend : cd frontend && npm run dev (Vite sur :5173, proxy /api → :8080).
CLI run.py
| Commande | Rôle |
|---|---|
run.py sync [source ...] |
ingestion — toutes les sources ou une liste |
run.py watch [minutes] |
boucle de resynchronisation (défaut 60) |
run.py serve [port] |
uvicorn louka.web:app (défaut 8080) |
run.py geocode [n] |
géocodage en lot (Adresses Québec, lots de 200) |
run.py geocode1 [n] |
géocodage un-par-un (Nominatim, 1 req/s) |
run.py poi [n] |
commodités OSM à proximité (Overpass) |
run.py quartier [n] |
enrichissement quartier (recensement, INSPQ, SPVM) |
run.py record <source> |
(ré)enregistre les fixtures HTTP d'un connecteur |
Variables d'environnement
run.py charge .env à la racine (parsing maison, aucune dépendance).
| Variable | Rôle |
|---|---|
FIRECRAWL_API_KEY |
rendu JavaScript / contournement Cloudflare (optionnel) |
SCRAPFLY_KEY |
scraping anti-bot ASP (optionnel) |
SESSION_SECRET |
signature HMAC des sessions |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET |
connexion Google OAuth |
KA_SSO_SECRET |
secret partagé du SSO KA (JWT HS256) |
KA_HUB_URL |
hub Groupe Ka (défaut https://www.groupe-ka.com) |
LOUKA_BASE_URL |
URL publique (derrière ngrok/proxy) |
LOUKA_*_DETAIL_LIMIT / LOUKA_*_MAX_PAGES |
limites par portail (Kijiji, LesPAC, RE/MAX…) |
VITE_MAPBOX_TOKEN |
jeton Mapbox du frontend (repli public codé en dur) |
Tests
.venv/bin/python -m pytestChaque connecteur est rejoué hors-ligne contre ses fixtures HTTP enregistrées (tests/fixtures/<source>/ + expected.json) ; la normalisation et le text mining ont leurs suites dédiées.
API (aperçu)
| Endpoint | Description |
|---|---|
GET /api/listings |
recherche filtrée : city, sector, unit_type, source, price_min/max, pets, furnished, available_by, area_min, q, limit, offset |
GET /api/listings.geojson?bbox=O,S,E,N |
FeatureCollection pour la carte (+ totalGeocoded, totalMatching) |
GET /api/listings/{uid} |
fiche complète : images, POI, quartier, digest, historique de prix |
GET /api/listings/{uid}/pdf |
fiche de propriété PDF (QR code) |
GET /api/facets |
villes, quartiers, types, sources — pour construire les filtres |
GET /api/stats / /api/stats/detailed / /api/stats/rapport.pdf |
agrégats du marché + rapport PDF |
GET /api/sources |
registre des 265 sources + compteurs + dernière synchro |
POST /api/sync |
synchronisation en arrière-plan (?source=) |
GET /api/auth/ka/login / /api/auth/google/login |
SSO KA & Google OAuth |
GET /api/me · /api/favorites · /api/org/* · /api/users/{ka_id} |
session, favoris, pages gestionnaires, profils publics |
Pages HTML servies par le SSR (louka/seo.py) : / · /villes · /ville/{slug}[/{type}] · /logement/{uid} (410 si retirée) · /g/{source} · /stats · /sources · /robots.txt · /sitemap*.xml
Déploiement
En production sur le nœud M3U96b (Mac Studio, cluster MacLustr), dans ~/apps/lou-ka, sous PM2 (3 processus) derrière ngrok :
| Processus PM2 | Commande | Rôle |
|---|---|---|
lou-ka-web |
.venv/bin/python run.py serve 8095 |
API + SSR + frontend sur le port 8095 |
lou-ka-sync |
.venv/bin/python run.py watch 60 |
resynchronisation horaire des sources |
lou-ka-ngrok |
ngrok http --url=www.lou-ka.com 8095 |
tunnel → https://www.lou-ka.com |
Philosophie d'exploitation : on ne pousse que le code — le serveur maintient ses données lui-même (data/*.db, frontend/dist et .env ne sont jamais versionnés).
Développement remote-first (IMPORTANT)
La source de vérité est le repo git sur le nœud M3U96b (~/apps/lou-ka), pas une copie locale sur le laptop. Toute modification se fait sur le nœud via SSH : édition, build frontend, pm2 restart lou-ka-web, puis commit/push depuis le nœud.
- Remote
origin= spbgit — le serveur git personnel git.spboucher.ai (bare repo~/srv/git/lou-ka.gitsur M3U96a, alias SSHgitsrv). Pas GitHub. - Le push fonctionne grâce à l'agent forwarding SSH actif pendant une session depuis le laptop.
Principes
- Politesse — délai ≥ 0,5 s entre requêtes, garde-fous de crawl, User-Agent identifié (
LouKaBot, page de transparence/bot). - Fidélité — aucun prix inventé (
price = nullsi absent), aucune coordonnée devinée (géocodage validé ou rien). - Traçabilité — chaque fiche renvoie vers l'annonce originale via
/passerelle/{uid}. - Robustesse — un connecteur qui casse n'affecte jamais les autres ; le garde-fou anti-dérive suspend les retraits quand une source déraille.
- Vie privée — aucun traceur tiers, consentement honnête, profil public strictement opt-in.
Écosystème Groupe Ka
Lou-Ka fait partie des plateformes du Groupe Ka, aux côtés d'Immo-Ka (propriétés à vendre), Vrai-Prix et les autres univers ·Ka — avec en partage : le SSO KA (ka_id émis par le hub), le framework cartographique Ka Maps, le design system « éditorial sharp » et le géocodage Adresses Québec.
Simon-Pierre Boucher — contact@spboucher.ai · git.spboucher.ai
© 2026 — tous droits réservés.
Un service Groupe Ka





