# ----------------------------------------------------------------------------- # Lou-Ka — Agrégateur de logements à louer (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # connectors/vallensis.py : connecteur Vallensis (vallensis.ca) # Condos locatifs neufs à Salaberry-de-Valleyfield (503, rue # Jacques-Cartier) — bâtiments de 20 unités (647 à 1148 pi²). # WordPress/Elementor rendu serveur : la page d'accueil affiche une carte # par typologie (« 3 ½ / À partir de / 1550$/mois »). Granularité : # typologie (pas d'unités individuelles publiées). # ----------------------------------------------------------------------------- from __future__ import annotations import re from bs4 import BeautifulSoup from .base import BaseConnector from ..schema import Listing BASE = "https://vallensis.ca" ADDRESS = "503, rue Jacques-Cartier, Salaberry-de-Valleyfield" CITY = "Salaberry-de-Valleyfield" TYPE_RE = re.compile(r"^\d\s*½$") PRICE_RE = re.compile(r"(\d[\d\s ]{2,6})\$\s*/\s*mois", re.I) IMG_RE = re.compile(r'https://vallensis\.ca/wp-content/uploads/' r'[^"\s\\)]+\.(?:jpe?g|png|webp)', re.I) class VallensisConnector(BaseConnector): source_id = "vallensis" 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") lines = soup.get_text("\n", strip=True).split("\n") images = [u for u in dict.fromkeys(IMG_RE.findall(html)) if not re.search(r"logo|icon|favicon|plan|" r"-\d{2,3}x\d{2,3}\.", u, re.I)][:15] # cartes « 3 ½ / À partir de / 1550$/mois » (dans l'ordre du texte) for i, line in enumerate(lines): if not TYPE_RE.match(line.strip()): continue window = " ".join(lines[i + 1:i + 4]) m = PRICE_RE.search(window) if not m: continue unit_type = line.strip().replace(" ", "") try: price = float(m.group(1).replace(" ", "").replace(" ", "")) except ValueError: continue ext_id = f"valleyfield-{unit_type.replace('½', '.5')}" if any(l.external_id == ext_id for l in listings): continue listings.append(Listing( source=self.source_id, external_id=ext_id, url=BASE + "/", title=f"{unit_type} — Vallensis", address=ADDRESS, city=CITY, unit_type=unit_type, price=price, price_label=f"À partir de {m.group(0)}", description="Condos locatifs neufs réalisés par le Groupe " "Architecte PDA — bâtiments de 20 unités de 647 " "à 1148 pi², esprit épuré et moderne.", images=images, )) return listings