SPB Git

spb/lou-ka Public

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

HTML 99.7%
4.3 KB · 102 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/alias_appartements.py : connecteur Alias Appartements5#   (aliasappartements.com — rue des Mouettes, Jonquière, ville de Saguenay ;6#   Groupe Boudreault). Projet locatif neuf UNIQUE de 80 unités 4½ livré en7#   4 phases (2023-2025). WordPress + Elementor + JetElements rendu serveur,8#   page projet unique SANS fiches d'unités : le site publie le nombre9#   d'unités (« 80 unités »), le type (« 4 1/2 »), le loyer plancher10#   (« À partir de 1575$ »), l'adresse (h3 « Rue des Mouettes, Jonquière »),11#   les caractéristiques (carrousel JetElements) et des photos — disponibilités12#   par formulaire/contact. -> 1 annonce « catalogue » à granularité PROJET13#   (précédent girs/immeubles_guillot), availability vide, rien d'inventé ;14#   price via price_label (« à partir de », plus bas loyer).15#   external_id stable : rue-des-mouettes.16#   robots.txt Yoast ouvert (Disallow vide), sitemap public.17# -----------------------------------------------------------------------------18from __future__ import annotations1920import re2122from bs4 import BeautifulSoup2324from ..schema import Listing, normalize_unit_type25from .base import BaseConnector2627BASE = "https://www.aliasappartements.com"2829_UNITS_RE = re.compile(r"^\d+\s+unités$", re.I)30_TYPE_RE = re.compile(r"^([1-6])\s*(?:½|1/2)\s*$")31_PRICE_RE = re.compile(r"^à\s+partir\s+de\s+[\d\s]+\$", re.I)32_SKIP_IMG = re.compile(r"\.svg$|logo|icon|Group-\d+", re.I)333435class AliasAppartementsConnector(BaseConnector):36    source_id = "alias_appartements"37    request_delay = 0.638    max_images = 103940    def fetch(self) -> list[Listing]:41        soup = BeautifulSoup(self.get(BASE + "/").text, "html.parser")4243        units_txt, unit_type, price_label, address = "", "", "", ""44        for h in soup.select(".elementor-heading-title"):45            t = re.sub(r"\s+", " ", h.get_text(" ", strip=True))46            if not t:47                continue48            if _UNITS_RE.match(t):49                units_txt = t50            elif _TYPE_RE.match(t):51                unit_type = normalize_unit_type(t)52            elif _PRICE_RE.match(t) and not price_label:53                price_label = t54            elif h.name == "h3" and re.search(r"(?i)mouettes", t):55                address = t5657        if not address and not units_txt:58            return []                     # page méconnaissable : ne rien émettre5960        # caractéristiques : carrousel JetElements61        amenities: list[str] = []62        if units_txt:63            amenities.append(units_txt)64        for h5 in soup.select("h5.jet-carousel__item-title"):65            t = re.sub(r"\s+", " ", h5.get_text(" ", strip=True))66            if t and t not in amenities:67                amenities.append(t)6869        # paragraphes descriptifs (hors pied de page)70        paras: list[str] = []71        for p in soup.select(".elementor-widget-text-editor p"):72            t = re.sub(r"\s+", " ", p.get_text(" ", strip=True))73            if len(t) >= 100 and "©" not in t and len(paras) < 5:74                paras.append(t)75        description = "\n".join(paras)[:1600]7677        images: list[str] = []78        for img in soup.select("img[src*='/wp-content/uploads/']"):79            u = img["src"]80            if (u.startswith("http") and not _SKIP_IMG.search(u)81                    and u not in images):82                images.append(u)83                if len(images) >= self.max_images:84                    break8586        return [Listing(87            source=self.source_id,88            external_id="rue-des-mouettes",89            url=BASE + "/",90            title=f"Alias Appartements — {address or 'Rue des Mouettes, Jonquière'}",91            address=address or "Rue des Mouettes, Jonquière",92            sector="Jonquière",93            city="Saguenay",94            unit_type=unit_type,95            price=None,                    # déduit de price_label (« à partir de »)96            price_label=price_label,97            availability="",               # disponibilités par formulaire/contact98            description=description,99            amenities=amenities[:30],100            images=images,101        )]102