# Lou·Ka ### Tous les logements à louer du Québec. Un seul endroit. **[www.lou-ka.com](https://www.lou-ka.com)** ![Python](https://img.shields.io/badge/Python-3.14-141814?style=for-the-badge&logo=python&logoColor=d9f26b) ![FastAPI](https://img.shields.io/badge/FastAPI-API-141814?style=for-the-badge&logo=fastapi&logoColor=d9f26b) ![React](https://img.shields.io/badge/React_18-Vite_+_TS-141814?style=for-the-badge&logo=react&logoColor=d9f26b) ![SQLite](https://img.shields.io/badge/SQLite-storage-141814?style=for-the-badge&logo=sqlite&logoColor=d9f26b) ![PWA](https://img.shields.io/badge/PWA-mobile_ready-141814?style=for-the-badge&logoColor=d9f26b) ![Sources](https://img.shields.io/badge/sources_recens%C3%A9es-255-1c5c41?style=flat-square) ![Connecteurs](https://img.shields.io/badge/connecteurs_actifs-194-1c5c41?style=flat-square) ![Annonces](https://img.shields.io/badge/annonces_agr%C3%A9g%C3%A9es-8000%2B-1c5c41?style=flat-square) ![Couverture](https://img.shields.io/badge/couverture-tout_le_Qu%C3%A9bec-1c5c41?style=flat-square) *Agrégateur indépendant de logements locatifs — chaque annonce avec toutes ses photos, ses détails standardisés, et un lien direct vers l'annonce originale du gestionnaire. Toujours à jour, automatiquement.*
--- ## Pourquoi Lou-Ka ? Chercher un appartement au Québec, c'est ouvrir 70 sites web différents — chacun avec sa propre navigation, ses propres filtres, son propre 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. > 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. ## L'architecture en 30 secondes ```mermaid flowchart LR subgraph Sources["74 gestionnaires immobiliers"] S1["Logisco · Cogir · CAPREIT
Immostar · DMA · Laberge
Akelius · Devimco · Mondev
… 68 connecteurs actifs"] end subgraph LouKa["Lou-Ka"] C["Connecteurs
1 adaptateur / site"] --> N["Normalisation
schéma Listing unique"] N --> D[("SQLite
hash + diff")] D --> A["API FastAPI
/api/listings · /api/facets"] A --> F["React 18 + Vite
PWA mobile · thème clair"] end W["⏱ Watcher horaire
(PM2)"] -.-> C S1 --> C F --> U["🔑 Locataire"] ``` | Couche | Rôle | Fichiers | |---|---|---| | **Connecteurs** | 1 module Python par gestionnaire : HTML rendu serveur, API JSON internes (Building Stack, RealVuu, Planpoint, Rentsync, source.immo, JetEngine…), ou Firecrawl pour les sites derrière Cloudflare | `louka/connectors/*.py` | | **Schéma** | `Listing` standardisé : adresse, secteur, ville, type (3½…), prix, disponibilité, commodités, **toutes les images** | `louka/schema.py` | | **Diff engine** | Upsert par hash de contenu — nouvelle / modifiée / disparue (désactivée) | `louka/db.py` | | **API** | Filtres ville / secteur / taille / prix / gestionnaire / recherche, facettes, stats, déclencheur de sync | `louka/web.py` | | **Frontend** | Design « éditorial sharp » : Space Grotesk, ombres décalées, accent lime, ticker temps réel, bottom sheet mobile, galeries photos, PWA installable | `frontend/` | ## Démarrage rapide ```bash git clone https://github.com/spboucher-ai/lou-ka.git && cd lou-ka # Backend python3 -m venv .venv && .venv/bin/pip install -r requirements.txt # Frontend cd frontend && npm install && npm run build && cd .. # (optionnel) sites JavaScript/anti-bot echo "FIRECRAWL_API_KEY=fc-votre-cle" > .env # 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) ``` ## Ajouter un gestionnaire (≈ 30 lignes) L'enregistrement est **auto-découvrant** : déposez un module dans `louka/connectors/`, c'est tout — aucun fichier partagé à modifier. ```python # louka/connectors/mon_agence.py from ..schema import Listing, infer_city, normalize_unit_type, parse_price from .base import BaseConnector class MonAgenceConnector(BaseConnector): source_id = "mon_agence" def fetch(self) -> list[Listing]: html = self.get("https://mon-agence.ca/logements").text # throttlé, poli # ... parser les cartes, les fiches, les photos ... return [Listing( source=self.source_id, external_id="123", url="https://mon-agence.ca/logement/123", title="555, avenue Exemple", sector="Limoilou", city=infer_city("Limoilou"), unit_type=normalize_unit_type("4 1/2"), price=parse_price("1 250 $ / mois"), images=[...], )] ``` Puis : `.venv/bin/python run.py sync mon_agence` — et l'annonce apparaît sur le site, avec sa fiche, sa galerie et son lien source. Ajoutez l'entrée correspondante dans `data/sources.json` pour la page **Sources**. ## API | Endpoint | Description | |---|---| | `GET /api/listings?city=§or=&unit_type=&source=&price_min=&price_max=&q=` | Recherche filtrée, triée par prix | | `GET /api/listings/{uid}` | Fiche complète (toutes les images, commodités, source) | | `GET /api/facets` | Valeurs distinctes pour construire les filtres | | `GET /api/sources` | Registre des 74 gestionnaires + compteurs + dernière sync | | `GET /api/stats` | Totaux par région, loyer moyen, journal de synchronisation | | `POST /api/sync` | Déclenche une synchronisation en arrière-plan | ## Couverture **Ville de Québec & Lévis** — Logisco, Cogir, Groupe Laberge, Immostar, DMA/Locago, Groupe Dallaire, Trudel, Immeubles Roussin, Immeubles Simard, MSI, Gestipro, Logisma, Lafrance & Mathieu, SIB, SDG, SGIQ, GIM Côté, Logisbourg, Bribourg, Paul-E. Richard, Headway, Contraste, Appartements Urbains, Picard, Brochu, GParadis, CAPREIT, Lokalia, Immoappart, OK Louer, et une douzaine de complexes (Huma, Le Clif, Terra, La Klé, Sentinelle, Rivero, Viridi, Quartier les Éléments…). **Grand Montréal** — Akelius, InterRent, Boardwalk, Minto, MetCap, Realstar, Hazelview, Groupe Copley, Cromwell, Lynk/Olymbec, Trylon, Plan A, Lofts MTL, Axia, Mondev, Devimco, Collection Équinoxe (Batimo/EMD), Progim, Rentalys, UTILE, Werkliv, 1 Square Phillips, Firma, Le Domaine, Beaudoin, Denux, Gestion Montréal, Nid d'Amour, SHDM… Chaque source non-connectable est **documentée avec sa raison** dans `data/sources.json` (ex. : aucun prix affiché, inventaire vide, site placeholder). ## Production Déployé sous **PM2** (3 processus) derrière **ngrok** : ``` lou-ka-web .venv/bin/python run.py serve 8095 # API + frontend lou-ka-sync .venv/bin/python run.py watch 60 # resync horaire lou-ka-ngrok ngrok http --url=www.lou-ka.com 8095 # tunnel ``` Philosophie d'exploitation : **on ne pousse que le code — le serveur maintient ses données lui-même.** ## Principes 1. **Politesse** — délai ≥ 0,5 s entre requêtes, garde-fous de crawl, User-Agent identifié. 2. **Fidélité** — aucun prix inventé : si la source n'affiche pas de prix, `price = null`. 3. **Traçabilité** — chaque fiche renvoie vers l'annonce originale du gestionnaire. 4. **Robustesse** — un connecteur qui casse n'affecte jamais les autres (auto-découverte tolérante, try/except par annonce, journal `sync_log`). ---
## Auteur **Simon-Pierre Boucher** [![Email](https://img.shields.io/badge/contact@spboucher.ai-141814?style=for-the-badge&logo=minutemailer&logoColor=d9f26b)](mailto:contact@spboucher.ai) [![GitHub](https://img.shields.io/badge/spboucher--ai-141814?style=for-the-badge&logo=github&logoColor=d9f26b)](https://github.com/spboucher-ai) *Conçu, construit et déployé en une journée — de la recherche de marché (74 gestionnaires recensés et vérifiés) au produit en production.* © 2026 Simon-Pierre Boucher — tous droits réservés.