SPB Git forge

spb/lou-ka

Public

Lou·Ka — tous les logements à louer du Québec, un seul endroit.

232commits 1branches 0releases
172.9 MBsize
maindefault branch
2 days agolast push
HTML 98.9% Python 0.6%
4.5 KB · 109 lines python
Raw Blame History
1# -----------------------------------------------------------------------------2# Lou-Ka — Agrégateur de logements à louer (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/nelson.py : connecteur Le Nelson (lenelson.com)5#   Immeuble locatif de 5 étages au 111, rue Jean-Besré à Cowansville6#   (Brome-Missisquoi). Site PHP statique rendu serveur : une page par étage7#   (/appartements-a-louer/logements-a-louer-cowansville-<etage>) avec, pour8#   chaque unité, un lien porteur de data-attributes (data-appt, data-area,9#   data-room, data-bathroom, data-pdf, data-availability, data-type).10#   ⚠️ HTTPS obligatoire (les URL http bouclent en 301) et User-Agent complet11#   requis (406 sinon — géré par l'escalade de BaseConnector). Granularité :12#   unité ; on ne retient que celles non « LOUÉ ». Prix non publiés.13# -----------------------------------------------------------------------------14from __future__ import annotations1516import re1718from bs4 import BeautifulSoup1920from .base import BaseConnector21from ..schema import Listing, normalize_unit_type2223BASE = "https://lenelson.com"24FLOOR_PAGES = [25    ("premiere-etage", 1),26    ("deuxieme-etage", 2),27    ("troisieme-etage", 3),28    ("quatrieme-etage", 4),29    ("cinquieme-etage", 5),30]3132ADDRESS = "111, rue Jean-Besré, Cowansville"33CITY = "Cowansville"3435SQFT_RE = re.compile(r"(\d{3,4})")36UA = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "37      "(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36")383940class NelsonConnector(BaseConnector):41    source_id = "nelson"42    request_delay = 0.64344    def fetch(self) -> list[Listing]:45        listings: list[Listing] = []46        for slug, floor in FLOOR_PAGES:47            url = (f"{BASE}/appartements-a-louer/"48                   f"logements-a-louer-cowansville-{slug}")49            try:50                html = self.get(url, headers={"User-Agent": UA}).text51            except Exception:52                continue53            soup = BeautifulSoup(html, "html.parser")5455            for a in soup.select("a[data-appt]"):56                try:57                    unit_no = (a.get("data-appt") or "").strip()58                    if not unit_no:59                        continue60                    availability = (a.get("data-availability") or "").strip()61                    if re.search(r"lou[ée]", availability, re.I):62                        continue          # déjà loué63                    unit_type = normalize_unit_type(64                        (a.get("data-type") or "").strip())65                    sqft = None66                    ms = SQFT_RE.search(a.get("data-area") or "")67                    if ms:68                        sqft = float(ms.group(1))69                    bedrooms = bathrooms = None70                    if (a.get("data-room") or "").isdigit():71                        bedrooms = int(a["data-room"])72                    if (a.get("data-bathroom") or "").isdigit():73                        bathrooms = int(a["data-bathroom"])7475                    details: dict = {"floor": f"Étage {floor}"}76                    pdf = (a.get("data-pdf") or "").strip()77                    if pdf:78                        details["plan_pdf"] = (pdf if pdf.startswith("http")79                                               else BASE + pdf)80                    images: list[str] = []81                    thumb = (a.get("data-thumbnail") or "").strip()82                    if thumb and "default-thumbnail" not in thumb:83                        images.append(thumb if thumb.startswith("http")84                                      else BASE + thumb)8586                    ext_id = f"logement-{unit_no}"87                    if any(l.external_id == ext_id for l in listings):88                        continue89                    listings.append(Listing(90                        source=self.source_id,91                        external_id=ext_id,92                        url=url,93                        title=f"Logement {unit_no}"94                              + (f" ({unit_type})" if unit_type else "")95                              + " — Le Nelson",96                        address=ADDRESS,97                        city=CITY,98                        unit_type=unit_type,99                        bedrooms=bedrooms,100                        bathrooms=bathrooms,101                        availability=availability or "Disponible",102                        area_sqft=sqft,103                        details=details,104                        images=images,105                    ))106                except Exception:107                    continue108        return listings109