# ----------------------------------------------------------------------------- # Lou-Ka — Agrégateur de logements à louer (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # connectors/ocartier.py : connecteur OCartier 2 (ocartier.ca — TDR # Développements). Tour locative de 200 appartements au 30, rue St-Hubert # à Laval (métro Cartier, secteur Pont-Viau), loyer tout inclus # (électricité, chauffage, climatisation, eau, internet). # WordPress/Elementor rendu serveur : la page /plans/ affiche une # fourchette de prix par TYPOLOGIE (« Studios / 3 1/2 / 4 1/2 / 5 1/2 », # « À partir de X $ jusqu'à Y $ »). Granularité TYPOLOGIE. # ----------------------------------------------------------------------------- from __future__ import annotations import re from bs4 import BeautifulSoup from ..schema import Listing, normalize_unit_type from .base import BaseConnector BASE = "https://ocartier.ca" PAGE_URL = f"{BASE}/plans/" ADDRESS = "30, rue St-Hubert" CITY = "Laval" SECTOR = "Pont-Viau" BUILDING_AMENITIES = [ "Électricité incluse", "Chauffage inclus", "Climatisation incluse", "Eau incluse", "Internet inclus", ] TYPE_RE = re.compile(r"^(?:Studios?|\d\s*(?:1/2|½))$", re.I) RANGE_RE = re.compile( r"À partir de\s*([\d\s ,]+?)\s*\$.*?jusqu[’']à\s*([\d\s ,]+?)\s*\$", re.I | re.S) BED_RE = re.compile(r"(\d)\s*chambres?", re.I) IMG_RE = re.compile( r"https://ocartier\.ca/wp-content/uploads/[^\"'\s\\)]+?" r"\.(?:jpg|jpeg|png|webp)", re.I) def _num(txt: str) -> float | None: digits = re.sub(r"[^\d]", "", txt or "") return float(digits) if digits else None class OCartierConnector(BaseConnector): source_id = "ocartier" request_delay = 0.6 def fetch(self) -> list[Listing]: listings: list[Listing] = [] try: html = self.get(PAGE_URL).text except Exception: return listings soup = BeautifulSoup(html, "html.parser") # photos : la page /plans/ est pauvre en visuels, on complète avec la # galerie de la page /appartements/ (rendue serveur elle aussi) pool = IMG_RE.findall(html) try: pool += IMG_RE.findall(self.get(f"{BASE}/appartements/").text) except Exception: pass images = [u for u in dict.fromkeys(pool) if not re.search(r"logo|favicon|icon|metro|-\d+x\d+\.", u, re.I)][:20] seen: set[str] = set() # blocs typologie :