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%
3.9 KB · 103 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/jardins_anna.py : connecteur Les Jardins d'Anna (lesjardinsdanna.com)5#   Projet domiciliaire de Nicolet (Centre-du-Québec), à 15 minutes de6#   Trois-Rivières — logements 4½ et 5½ de style condo. WordPress rendu7#   serveur : la page /location/ présente les typologies en sections <h3>8#   (« Logements 4 ½ », « … avec verrière », « … avec verrière et garage »,9#   « Logements 5 ½ ») suivies de « À partir de 1 250$ par mois, nos …10#   incluent : » + liste d'inclusions. Granularité = typologie (le site ne11#   publie pas d'inventaire par unité). external_id = typologie en slug.12# -----------------------------------------------------------------------------13from __future__ import annotations1415import re1617from bs4 import BeautifulSoup1819from ..schema import Listing, normalize_unit_type, strip_accents20from .base import BaseConnector2122BASE = "https://lesjardinsdanna.com"23PAGE_URL = f"{BASE}/location/"2425PRICE_RE = re.compile(r"À\s+partir\s+de\s+([\d\s  ]{3,7})\$\s*par\s*mois", re.I)26UNIT_RE = re.compile(r"(\d)\s*(?:½|1/2)")27IMG_RE = re.compile(28    r"https?://lesjardinsdanna\.com/wp-content/uploads/[^\"'\s)]+"29    r"\.(?:jpg|jpeg|png|webp)", re.I)303132def _slug(text: str) -> str:33    s = strip_accents(text.lower())34    return re.sub(r"[^a-z0-9]+", "-", s).strip("-")353637def _num(txt: str) -> float | None:38    n = re.sub(r"[\s  ]", "", txt or "")39    try:40        return float(n)41    except ValueError:42        return None434445class JardinsAnnaConnector(BaseConnector):46    source_id = "jardins_anna"47    request_delay = 0.84849    def fetch(self) -> list[Listing]:50        html = self.get(PAGE_URL).text51        soup = BeautifulSoup(html, "html.parser")5253        images = [u for u in dict.fromkeys(IMG_RE.findall(html))54                  if not re.search(r"logo|icon|-\d+x\d+\.", u, re.I)][:15]5556        listings: list[Listing] = []57        for h3 in soup.find_all("h3"):58            label = re.sub(r"\s+", " ", h3.get_text(" ", strip=True))59            if not re.match(r"Logements?\s", label, re.I):60                continue61            # texte de la section : frères suivants jusqu'au prochain h362            price = None63            price_label = ""64            amenities: list[str] = []65            node = h366            while True:67                node = node.find_next_sibling()68                if node is None or node.name == "h3":69                    break70                text = re.sub(r"\s+", " ", node.get_text(" ", strip=True))71                if not price:72                    pm = PRICE_RE.search(text)73                    if pm:74                        price = _num(pm.group(1))75                        price_label = pm.group(0)76                for li in node.find_all("li"):77                    item = re.sub(r"\s+", " ", li.get_text(" ", strip=True))78                    if item and item not in amenities and len(item) < 80:79                        amenities.append(item)80            if price is None:81                continue82            unit_type = ""83            um = UNIT_RE.search(label)84            if um:85                unit_type = normalize_unit_type(f"{um.group(1)} ½")86            listings.append(Listing(87                source=self.source_id,88                external_id=_slug(label),89                url=PAGE_URL,90                title=f"{label} — Les Jardins d'Anna",91                address="Les Jardins d'Anna, Nicolet",92                sector="",93                city="Nicolet",94                unit_type=unit_type,95                price=price,96                price_label=price_label,97                description=f"{label} de style condo aux Jardins d'Anna, "98                            "Nicolet (à 15 minutes de Trois-Rivières).",99                amenities=amenities,100                images=list(images),101            ))102        return listings103