# ----------------------------------------------------------------------------- # Home-Ka — US real-estate aggregator (Groupe KA) # Author: Simon-Pierre Boucher — contact@spboucher.ai # connectors/json/jsonld_site.py : schema.org JSON-LD site connector. # # Many brokerage sites (Real Estate Webmasters and other server-rendered # platforms) publish each listing as JSON-LD (`Product` + `RealEstateListing` # / `SingleFamilyResidence` nodes) — clean, stable data with full address, # geo, price and photos, no proxy needed. This is the SECONDARY crawler path # (authority 3): use it to bootstrap coverage while converting the brokerage # to a direct feed. Ported from immo-ka connectors/jsonld.py. # # {"id": "michael_saunders", "connector_type": "jsonld", "config": { # "base_url": "https://www.michaelsaunders.com", # "url_include": "/listing/", # "external_id_regex": "/listing/([A-Z0-9-]+)/", # from the URL # "max_pages": 400, "static": {"state": "FL"}}} # ----------------------------------------------------------------------------- from __future__ import annotations import hashlib import json import re import time from ..base import BaseConnector from ...normalize import parse_price from ...schema import Listing _LD_RE = re.compile(r']+type=["\']application/ld\+json["\'][^>]*>(.*?)', re.S | re.I) _LOC_RE = re.compile(r"\s*(?:)?\s*", re.S | re.I) _LISTING_TYPES = { "realestatelisting", "singlefamilyresidence", "house", "residence", "apartment", "product", "accommodation", "condominium", "townhouse", # some SSR platforms (Repliers/Next.js) mark the listing as a plain Place; # safe here because pages are pre-filtered by url_include "place", } # URL slug fallback: ".../7039-s-straight-avenue-homosassa-fl-34446/" _SLUG_ADDR_RE = re.compile(r"/([\w-]+)-([a-z]{2})-(\d{5})/?$", re.I) # `name` fallback: several platforms (Real Geeks, @properties/Christie's) # put the full address in Product.name — "123 Main St #4, Austin, TX 78701" _NAME_ADDR_RE = re.compile( r"^(.+?),\s*([^,]+?),\s*([A-Za-z]{2})[,\s]+(\d{5})(?:-\d{4})?\s*$") # Next.js "flight" streaming: some SSR sites (Douglas Elliman) embed the # JSON-LD as self.__next_s.push([0,{"type":"application/ld+json", # "children":""}]) instead of a