# ----------------------------------------------------------------------------- # Lou-Ka — Agrégateur de logements à louer (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # connectors/beloeil_94.py : connecteur Le 94 Vieux-Beloeil (94beloeil.ca) # Immeuble unique au 915, rue Guertin (Vieux-Beloeil) : commerce au RDC et # 8 appartements 3½ (1 c.c.) au-dessus. Site vitrine statique (constructeur # maison, rendu serveur) : une seule offre affichée sous forme de bandeau # « 3 1/2 disponible » + liste des inclusions. Granularité : # typologie (une annonce 3½ quand l'immeuble affiche une disponibilité). # ----------------------------------------------------------------------------- from __future__ import annotations import re from bs4 import BeautifulSoup from .base import BaseConnector from ..schema import Listing BASE = "https://www.94beloeil.ca" ADDRESS = "915, rue Guertin, Beloeil" # « 3 1/2 disponible Août 2026 » (bandeau d'accueil) AVAIL_RE = re.compile(r"(\d\s*(?:1/2|½))\s*disponibles?\s*([\w'’éûà]+\s*\d{4}|" r"maintenant|imm[ée]diatement)", re.I) SQFT_RE = re.compile(r"(\d{3,4})\s*(?:à\s*(\d{3,4})\s*)?pieds?\s*carr[ée]s", re.I) IMG_RE = re.compile(r'(?:src|href)="(/uploads/[^"]+\.(?:jpe?g|png|webp))"', re.I) class Beloeil94Connector(BaseConnector): source_id = "beloeil_94" request_delay = 0.6 def fetch(self) -> list[Listing]: listings: list[Listing] = [] try: html = self.get(BASE + "/").text except Exception: return listings soup = BeautifulSoup(html, "html.parser") text = soup.get_text("\n", strip=True) m = AVAIL_RE.search(text) if not m: return listings # rien d'affiché = aucune disponibilité unit_type = m.group(1).replace(" ", " ") availability = f"Disponible {m.group(2)}" # superficie « 685 à 747 pieds carrés » (fourchette de l'immeuble) sqft = None ms = SQFT_RE.search(text) if ms: try: sqft = float(ms.group(1)) except ValueError: sqft = None # inclusions : liste à puces autour de la fiche de l'immeuble amenities: list[str] = [] lines = text.split("\n") try: start = next(i for i, l in enumerate(lines) if "électro inclus" in l) for l in lines[start:start + 25]: if 3 < len(l) < 60 and not l.startswith(("Pour ", "Un ")): amenities.append(l) except StopIteration: pass images = [BASE + u for u in dict.fromkeys(IMG_RE.findall(html)) if not re.search(r"logo|icon|-\[converted\]", u, re.I)][:15] listings.append(Listing( source=self.source_id, external_id="guertin-3.5", url=BASE + "/", title="3½ — Le 94 Vieux-Beloeil", address=ADDRESS, sector="Vieux-Beloeil", city="Beloeil", unit_type="3½", bedrooms=1, bathrooms=1, availability=availability, area_sqft=sqft, description="Immeuble construit en 2020 avec commerce au RDC et " "8 unités d'une chambre à coucher au-dessus. " "Design moderne et lumineux.", amenities=list(dict.fromkeys(amenities))[:15], images=images, )) return listings