SPB Git forge

spb/lou-ka

Public

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

232commits 1branches 0releases
172.9 MBsize
maindefault branch
2 days agolast push
HTML 98.9% Python 0.6%
4.4 KB · 108 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/place_barrage.py : connecteur Place du Barrage (placedubarrage.com)5#   Immeuble locatif au 2, rue Vertu à Windsor (Estrie), géré par Sphère6#   immobilier. Site Wix rendu serveur : la page /appartements liste 24 cartes7#   (répéteur Wix « wixui-repeater__item ») — « Appartement 101-A », courte8#   description, prix (« 1475$ ») ou « LOUÉ », lien vers la fiche9#   /appartements/appartement-<no>-<lettre>. ⚠️ Le <title> du gabarit Wix10#   mentionne un autre projet (Le Connaught) : ignorer. On dérive l'URL de la11#   fiche du numéro affiché sur la carte (les hrefs peuvent être désalignés).12#   Granularité : unité ; prix affichés, typologies non publiées.13# -----------------------------------------------------------------------------14from __future__ import annotations1516import re1718from bs4 import BeautifulSoup1920from .base import BaseConnector21from ..schema import Listing2223BASE = "https://www.placedubarrage.com"24LIST_URL = f"{BASE}/appartements"2526ADDRESS = "2, rue Vertu, Windsor"27CITY = "Windsor"2829UNIT_RE = re.compile(r"^Appartement\s+(\d{3})-([A-F])$", re.I)30PRICE_RE = re.compile(r"^(\d{3,4})\s*\$$")31IMG_RE = re.compile(r'https://static\.wixstatic\.com/media/'32                    r'[^"\s\\)]+\.(?:jpe?g|png|webp)[^"\s\\)]*', re.I)3334FLOOR_BY_DIGIT = {"1": "Étage 1", "2": "Étage 2", "3": "Étage 3"}353637class PlaceBarrageConnector(BaseConnector):38    source_id = "place_barrage"39    request_delay = 0.64041    def fetch(self) -> list[Listing]:42        listings: list[Listing] = []43        try:44            html = self.get(LIST_URL).text45        except Exception:46            return listings47        soup = BeautifulSoup(html, "html.parser")4849        for item in soup.select("div.wixui-repeater__item"):50            try:51                text_lines = [t.strip() for t in52                              item.get_text("\n", strip=True).split("\n")]53                unit_no, letter = "", ""54                for t in text_lines:55                    m = UNIT_RE.match(t)56                    if m:57                        unit_no, letter = m.group(1), m.group(2).upper()58                        break59                if not unit_no:60                    continue61                if any(re.search(r"^lou[ée]$", t, re.I) for t in text_lines):62                    continue          # déjà loué63                price = None64                for t in text_lines:65                    mp = PRICE_RE.match(t)66                    if mp:67                        price = float(mp.group(1))68                        break69                if price is None:70                    continue          # ni prix ni LOUÉ : statut inconnu7172                description = next(73                    (t for t in text_lines74                     if t.lower().startswith("appartement situé")), "")7576                images = [u.split("/v1/")[0] for u in77                          dict.fromkeys(IMG_RE.findall(str(item)))][:15]7879                # ⚠️ hrefs Wix désalignés (la carte 302-E pointe vers la fiche80                # 302-a…) et seules les fiches -a/-b/-f existent : on ne garde81                # la fiche que si sa lettre correspond à celle de la carte,82                # sinon l'URL de l'annonce est la page liste.83                url = LIST_URL84                a = item.select_one('a[href*="/appartements/appartement-"]')85                if a and a.get("href", "").rstrip("/").endswith(86                        f"-{unit_no}-{letter.lower()}"):87                    url = a["href"].rstrip("/")88                ext_id = f"{unit_no}-{letter}".lower()89                if any(l.external_id == ext_id for l in listings):90                    continue91                listings.append(Listing(92                    source=self.source_id,93                    external_id=ext_id,94                    url=url,95                    title=f"Appartement {unit_no}-{letter} — "96                          f"Place du Barrage",97                    address=ADDRESS,98                    city=CITY,99                    price=price,100                    availability="Disponible",101                    description=description,102                    details={"floor": FLOOR_BY_DIGIT.get(unit_no[0], "")},103                    images=images,104                ))105            except Exception:106                continue107        return listings108