diff --git a/makelaars.md b/makelaars.md index 8685220..1bcbab1 100644 --- a/makelaars.md +++ b/makelaars.md @@ -4,8 +4,8 @@ | Done | Naam | Website | Adres | | [ ] | ---- |------|---------|-------| -| [ ] | Van Silfhout & Hogetoorn Wereldmakelaars | vansilfhout.nl | Ireneboulevard 2 | -| [ ] | Van Daal Makelaardij | vandaalmakelaardij.nl | Voldersgracht 33 | +| [x] | Van Silfhout & Hogetoorn Wereldmakelaars | vansilfhout.nl | Ireneboulevard 2 | +| [x] | Van Daal Makelaardij | vandaalmakelaardij.nl | Voldersgracht 33 | | [x] | Björnd Makelaardij | bjornd.nl | Oude Delft 103 | | [ ] | Hof van Delft Makelaardij | hofvandelftmakelaardij.nl | Wateringsevest 26 | | [ ] | V&W Makelaars Delft | vwmakelaars.nl | Coenderstraat 31 | diff --git a/src/adapters/api.py b/src/adapters/api.py index 450d003..79674d2 100644 --- a/src/adapters/api.py +++ b/src/adapters/api.py @@ -244,6 +244,69 @@ def fetch_moerman() -> list[RawListing]: return listings +# --------------------------------------------------------------------------- +# Van Daal Makelaardij (Delft) +# --------------------------------------------------------------------------- +# OG Online / realtime-listings platform. + +_VANDAAL_BASE = "https://www.vandaalmakelaardij.nl" +_VANDAAL_SKIP = {"rented", "rented_ur"} + +_VANDAAL_STATUS_MAP = { + "available": "beschikbaar", + "under_bid": "onder_bod", + "under_option": "onder_bod", + "is_bought": "verkocht", + "sold": "verkocht", + "sold_ur": "verkocht", +} + + +def fetch_vandaal() -> list[RawListing]: + data = fetch_json( + f"{_VANDAAL_BASE}/nl/realtime-listings/consumer", + headers={"X-Requested-With": "XMLHttpRequest"}, + ) + + listings = [] + for item in data: + if not item.get("isSales"): + continue + if item.get("statusOrig") in _VANDAAL_SKIP: + continue + if item.get("salesPrice", 0) > config.MAX_PRICE: + continue + + postcode = (item.get("zipcode") or "").replace(" ", "") or None + perceel = item.get("plotSurface") or None + if perceel == 0: + perceel = None + + raw_year = item.get("dateOfConstruction") or "" + bouwjaar = int(raw_year) if raw_year.isdigit() else None + + listings.append(RawListing( + url=_VANDAAL_BASE + item["url"], + source_makelaar="vandaal", + status=_VANDAAL_STATUS_MAP.get(item.get("statusOrig", ""), "beschikbaar"), + adres=item.get("address") or None, + postcode=postcode, + stad=item.get("city") or None, + prijs=item.get("salesPrice") or None, + woningtype=item.get("type") or None, + woonoppervlak=item.get("livingSurface") or None, + perceeloppervlak=perceel, + kamers=item.get("rooms") or None, + slaapkamers=item.get("bedrooms") or None, + bouwjaar=bouwjaar, + energielabel=item.get("energyLabel") or None, + hero_image_url=item.get("photo") or None, + )) + + log.info("vandaal: %d koopwoningen opgehaald", len(listings)) + return listings + + # --------------------------------------------------------------------------- # SCRAPERS — exporteer hier alle actieve API adapters # --------------------------------------------------------------------------- @@ -252,4 +315,5 @@ SCRAPERS = { 'bjornd': fetch_bjornd, 'ooms': fetch_ooms, 'moerman': fetch_moerman, + 'vandaal': fetch_vandaal, } diff --git a/src/adapters/ssr.py b/src/adapters/ssr.py index 1f24630..a4e9ba5 100644 --- a/src/adapters/ssr.py +++ b/src/adapters/ssr.py @@ -1038,6 +1038,148 @@ def fetch_schielandborsboom() -> list[RawListing]: return listings +# --------------------------------------------------------------------------- +# Van Silfhout & Hogetoorn Wereldmakelaars (Delft) +# --------------------------------------------------------------------------- + +_VANSILFHOUT_BASE = "https://www.vansilfhout.nl" + +_VANSILFHOUT_STATUS_MAP = { + "te koop": "beschikbaar", + "onder bod": "onder_bod", + "verkocht onder voorbehoud": "verkocht", + "verkocht": "verkocht", +} + + +def _vansilfhout_detail(detail_url: str) -> dict: + """Fetch Van Silfhout detail page; extract postcode from JS and specs from shortSpecs.""" + try: + import re as _re + r = __import__("httpx").get( + detail_url, + headers={"User-Agent": config.USER_AGENT}, + timeout=15, + follow_redirects=True, + ) + r.raise_for_status() + html = r.text + from bs4 import BeautifulSoup as _BS + soup = _BS(html, "html.parser") + + # Postcode embedded in JS: objectZipcode': '2624NP' + m = _re.search(r"objectZipcode':\s*'([^']+)'", html) + postcode = m.group(1) if m else None + + # shortSpecs: