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/inovario.py : connecteur Inovario (inovario.com)5# Promoteur-gestionnaire de Châteauguay — deux projets locatifs : rue6# St-Denis et Quartier du Centre Signature. WordPress/Elementor rendu7# serveur : le sitemap Yoast (/sitemap_index.xml → page-sitemap.xml) liste8# une page par modèle d'appartement (chateauguay-<projet>-<type>[-modele-…]).9# Pas de prix ni d'unités individuelles publiés. Granularité : modèle10# (typologie).11# -----------------------------------------------------------------------------12from __future__ import annotations1314import re1516from bs4 import BeautifulSoup1718from .base import BaseConnector19from ..schema import Listing, normalize_unit_type2021BASE = "https://inovario.com"22SITEMAP = f"{BASE}/page-sitemap.xml"2324CITY = "Châteauguay"2526LOC_RE = re.compile(r"<loc>([^<]+)</loc>")27MODEL_RE = re.compile(r"^https://inovario\.com/(chateauguay-[a-z0-9-]+)/?$")28TYPE_RE = re.compile(r"(\d)-(?:1-2|et-demi)")29SQFT_RE = re.compile(r"(\d{3,4})-pc")30IMG_RE = re.compile(r'https://inovario\.com/wp-content/uploads/'31 r'[^"\s\\)]+\.(?:jpe?g|png|webp)', re.I)323334class InovarioConnector(BaseConnector):35 source_id = "inovario"36 request_delay = 0.63738 def fetch(self) -> list[Listing]:39 listings: list[Listing] = []40 try:41 xml = self.get(SITEMAP).text42 except Exception:43 return listings4445 for url in LOC_RE.findall(xml):46 m = MODEL_RE.match(url.strip())47 if not m:48 continue49 slug = m.group(1)50 mt = TYPE_RE.search(slug)51 if not mt:52 continue # pages de projet/hub, pas des modèles53 unit_type = normalize_unit_type(f"{mt.group(1)}½")5455 if "st-denis" in slug:56 project = "Rue St-Denis"57 address = "rue Saint-Denis, Châteauguay"58 else:59 project = "Quartier du Centre Signature"60 address = "rue Principale, Châteauguay"6162 sqft = None63 ms = SQFT_RE.search(slug)64 if ms:65 sqft = float(ms.group(1))6667 title, amenities, images = "", [], []68 try:69 html = self.get(url).text70 soup = BeautifulSoup(html, "html.parser")71 h1 = soup.find("h1")72 if h1:73 title = h1.get_text(" ", strip=True)74 # « CARACTÉRISTIQUES DE NOS APPARTEMENTS » : liste à puces75 for li in soup.select("ul li"):76 t = li.get_text(" ", strip=True)77 if 3 < len(t) < 90 and not li.find("a"):78 amenities.append(t)79 images = [u for u in dict.fromkeys(IMG_RE.findall(html))80 if not re.search(r"logo|icon|favicon", u, re.I)][:15]81 except Exception:82 pass8384 variant = ""85 mv = re.search(r"(modele-[\w-]+|coin|centre|den)$", slug)86 if mv:87 variant = mv.group(1).replace("modele-", "modèle ")8889 listings.append(Listing(90 source=self.source_id,91 external_id=slug,92 url=url,93 title=title or f"{unit_type} — {project}, Châteauguay",94 address=address,95 city=CITY,96 unit_type=unit_type,97 area_sqft=sqft,98 availability="",99 details={"project": project,100 **({"variant": variant} if variant else {})},101 description=f"Appartement {unit_type} de style condo du "102 f"projet {project} à Châteauguay : planchers de "103 f"béton insonorisés, aires ouvertes et grande "104 f"fenestration. Prix sur demande.",105 amenities=list(dict.fromkeys(amenities))[:15],106 images=images,107 ))108 return listings109