spb/immo-ka Public
Immo-Ka — agrégateur des propriétés à vendre au Québec (73 connecteurs, ~40 000 annonces, React+FastAPI)
Python 67.2%
TypeScript 19.4%
CSS 12.9%
HTML 0.5%
1# -----------------------------------------------------------------------------2# Immo-Ka — Agrégateur de maisons à vendre (province de Québec)3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/jsonld.py : utilitaires partagés d'extraction schema.org (JSON-LD)5# Beaucoup de sites d'agences (Sutton, et d'autres CMS immobiliers) publient6# la liste des propriétés en JSON-LD `ItemList` — données propres et stables.7# -----------------------------------------------------------------------------8from __future__ import annotations910import json11import re1213_LD_RE = re.compile(r'<script[^>]+type="application/ld\+json"[^>]*>(.*?)</script>',14 re.S | re.I)151617def iter_ld(html: str):18 """Itère les objets JSON-LD d'une page (chaque bloc, aplati depuis @graph)."""19 for block in _LD_RE.findall(html):20 block = block.strip()21 if not block:22 continue23 try:24 data = json.loads(block)25 except ValueError:26 continue27 nodes = data.get("@graph", [data]) if isinstance(data, dict) else data28 for node in (nodes if isinstance(nodes, list) else [nodes]):29 if isinstance(node, dict):30 yield node313233def item_list_elements(html: str) -> list[dict]:34 """Retourne les `itemListElement` (schema.org ItemList) trouvés dans la page."""35 out: list[dict] = []36 for node in iter_ld(html):37 elems = node.get("itemListElement")38 if isinstance(elems, list):39 out.extend(e for e in elems if isinstance(e, dict))40 # certaines pages imbriquent l'ItemList sous mainEntity41 me = node.get("mainEntity")42 if isinstance(me, dict) and isinstance(me.get("itemListElement"), list):43 out.extend(e for e in me["itemListElement"] if isinstance(e, dict))44 return out454647def sqm_to_sqft(value) -> float | None:48 try:49 return round(float(value) * 10.7639)50 except (TypeError, ValueError):51 return None52