Lou·Ka — tous les logements à louer du Québec, un seul endroit.
HTML 98.9%
Python 0.6%
1# -----------------------------------------------------------------------------2# Lou-Ka — Agrégateur de logements à louer (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/solaris.py : connecteur Solaris Boisbriand (solarisboisbriand.com)5# Condos locatifs neufs au 1030, rue des Francs-Bourgeois, Boisbriand6# (Faubourg Boisbriand). Site Wix mais rendu SERVEUR : la page /plans contient7# dans son HTML statique les cartes « n ½ (x chambres) … à partir de NNNN$/mois »8# (texte riche wixui, entités ½ / à). Aucune liste d'unités —9# granularité : TYPOLOGIE (3 fiches : 3½ / 4½ / 5½, prix « à partir de »).10# -----------------------------------------------------------------------------11from __future__ import annotations1213import html as _html14import re1516from ..schema import Listing17from .base import BaseConnector1819BASE = "https://www.solarisboisbriand.com"20PLANS_URL = f"{BASE}/plans"2122ADDRESS = "1030, rue des Francs-Bourgeois, Boisbriand"23CITY = "Boisbriand"2425# « 3 ½ (1 chambre) … à partir de 1800$/mois » (texte décodé, balises retirées)26CARD_RE = re.compile(27 r"(\d)\s*½\s*\((\d)\s*chambres?\)" # typologie (n ½ (x chambres))28 r".{0,400}?à\s*partir\s*de\s*" # libellé29 r"(\d[\d\s,]*)\s*\$\s*/\s*mois", re.S)30IMG_RE = re.compile(31 r"https://static\.wixstatic\.com/media/[^\"'\s\\)]+"32 r"\.(?:jpe?g|png|webp)", re.I)33SKIP_IMG_RE = re.compile(r"logo|favicon|icon|blur|~mv2\.png", re.I)343536class SolarisConnector(BaseConnector):37 source_id = "solaris"38 request_delay = 0.639 max_images = 104041 def _images(self) -> list[str]:42 """Photos du projet (page d'accueil Wix, media wixstatic)."""43 try:44 html = self.get(f"{BASE}/").text45 except Exception:46 return []47 out: list[str] = []48 for u in dict.fromkeys(IMG_RE.findall(html)):49 u = u.split("/v1/")[0] # version pleine taille50 if not SKIP_IMG_RE.search(u) and u not in out:51 out.append(u)52 return out[: self.max_images]5354 def fetch(self) -> list[Listing]:55 raw = self.get(PLANS_URL).text56 # entités (½, à…) puis balises -> texte plat57 text = _html.unescape(raw)58 text = re.sub(r"<script.*?</script>", " ", text, flags=re.S)59 text = re.sub(r"<style.*?</style>", " ", text, flags=re.S)60 text = re.sub(r"<[^>]+>", " ", text)61 text = re.sub(r"\s+", " ", text)6263 images = self._images()64 listings: list[Listing] = []65 seen: set[str] = set()66 for n, beds, amount in CARD_RE.findall(text):67 unit_type = f"{n}½"68 if unit_type in seen:69 continue70 seen.add(unit_type)71 amount = amount.strip().replace(" ", "").replace(",", "")72 try:73 price = float(amount)74 except ValueError:75 continue76 if not 300 <= price <= 20000:77 continue78 listings.append(Listing(79 source=self.source_id,80 external_id=f"solaris-{n}.5",81 url=PLANS_URL,82 title=f"Solaris Boisbriand — {unit_type}"83 f" ({beds} chambre{'s' if beds != '1' else ''})",84 address=ADDRESS,85 city=CITY,86 unit_type=unit_type,87 bedrooms=float(beds),88 price=price,89 price_label=f"à partir de {int(price)}$/mois",90 availability="Disponible",91 description=f"Condo locatif neuf {unit_type}"92 f" ({beds} chambre(s)) au Faubourg Boisbriand"93 " — prix « à partir de », selon l'unité.",94 images=list(images),95 ))96 return listings97