SPB Git

spb/lou-ka Public

Lou·Ka — tous les logements à louer du Québec, un seul endroit.

HTML 99.7%
3.4 KB · 81 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Lou-Ka — Agrégateur de logements à louer (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/immeubles_bois.py : connecteur Les Immeubles Bois (immeublesbois.ca5#   — Rimouski, Bas-Saint-Laurent ; 320+ logements, 26 immeubles, 1½–5½).6#   WordPress + Elementor rendu serveur : la page /residentiel/ liste un7#   loop-item par immeuble (adresse en h6 + photo aérienne + lien fiche).8#   Les fiches d'immeuble ne publient RIEN de plus (titre + « Demander une9#   visite » + vignettes d'AUTRES immeubles) : elles ne sont pas visitées —10#   1 seule requête par sync. Granularité IMMEUBLE assumée (précédent girs /11#   immeubles_guillot) : aucun prix, aucune unité, aucune disponibilité12#   publiés -> price=None, availability vide, rien d'inventé. Tout le parc13#   affiché est à Rimouski (coordonnées du siège : 40 rue Saint-Germain Est).14#   external_id stable : slug de la fiche (/157-159-rue-hudon/).15#   robots.txt : Allow / pour les agents génériques (blocage ciblé de bots16#   d'entraînement IA ; Content-Signal search=yes, use=reference — usage17#   agrégateur/référence avec lien retour cohérent, précédent place_florimay).18# -----------------------------------------------------------------------------19from __future__ import annotations2021import re2223from bs4 import BeautifulSoup2425from ..schema import Listing26from .base import BaseConnector2728BASE = "https://immeublesbois.ca"29LIST_URL = f"{BASE}/residentiel/"303132class ImmeublesBoisConnector(BaseConnector):33    source_id = "immeubles_bois"34    request_delay = 0.63536    def fetch(self) -> list[Listing]:37        soup = BeautifulSoup(self.get(LIST_URL).text, "html.parser")3839        listings: dict[str, Listing] = {}40        for item in soup.select(41                '[data-elementor-type="loop-item"].category-residentiel'):42            try:43                self._parse_item(item, listings)44            except Exception:45                continue46        return list(listings.values())4748    # -- loop-item Elementor (un immeuble) ----------------------------------------49    def _parse_item(self, item, listings: dict[str, Listing]) -> None:50        link = item.select_one("h6 a[href]")51        if link is None:52            return53        url = link["href"].strip()54        title = re.sub(r"\s+", " ", link.get_text(" ", strip=True))55        m = re.search(r"immeublesbois\.ca/([^/?#]+)", url)56        if not m or not title:57            return58        ext_id = m.group(1)59        if ext_id in listings:60            return6162        images: list[str] = []63        img = item.select_one("img[src]")64        if img and img["src"].startswith("http") \65                and "logo" not in img["src"].lower():66            images.append(img["src"])6768        listings[ext_id] = Listing(69            source=self.source_id,70            external_id=ext_id,71            url=url,72            title=title,73            address=f"{title}, Rimouski",74            city="Rimouski",              # tout le parc affiché est à Rimouski75            unit_type="",                  # typologies non publiées par immeuble76            price=None,                    # aucun loyer publié sur le site77            price_label="",78            availability="",               # disponibilités hors ligne (téléphone)79            images=images,80        )81