# ----------------------------------------------------------------------------- # Immo-Ka — Agrégateur de maisons à vendre (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # connectors/jsonld.py : utilitaires partagés d'extraction schema.org (JSON-LD) # Beaucoup de sites d'agences (Sutton, et d'autres CMS immobiliers) publient # la liste des propriétés en JSON-LD `ItemList` — données propres et stables. # ----------------------------------------------------------------------------- from __future__ import annotations import json import re _LD_RE = re.compile(r']+type="application/ld\+json"[^>]*>(.*?)', re.S | re.I) def iter_ld(html: str): """Itère les objets JSON-LD d'une page (chaque bloc, aplati depuis @graph).""" for block in _LD_RE.findall(html): block = block.strip() if not block: continue try: data = json.loads(block) except ValueError: continue nodes = data.get("@graph", [data]) if isinstance(data, dict) else data for node in (nodes if isinstance(nodes, list) else [nodes]): if isinstance(node, dict): yield node def item_list_elements(html: str) -> list[dict]: """Retourne les `itemListElement` (schema.org ItemList) trouvés dans la page.""" out: list[dict] = [] for node in iter_ld(html): elems = node.get("itemListElement") if isinstance(elems, list): out.extend(e for e in elems if isinstance(e, dict)) # certaines pages imbriquent l'ItemList sous mainEntity me = node.get("mainEntity") if isinstance(me, dict) and isinstance(me.get("itemListElement"), list): out.extend(e for e in me["itemListElement"] if isinstance(e, dict)) return out def sqm_to_sqft(value) -> float | None: try: return round(float(value) * 10.7639) except (TypeError, ValueError): return None