first setup, travel works, bjornd api works
This commit is contained in:
116
src/adapters/api.py
Normal file
116
src/adapters/api.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
adapters/api.py — JSON/API-based makelaars
|
||||
|
||||
Elke scraper is een functie () -> list[RawListing].
|
||||
Voeg nieuwe toe onderaan en registreer in SCRAPERS.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
|
||||
import httpx
|
||||
|
||||
import config
|
||||
from huizenbot import RawListing
|
||||
|
||||
log = logging.getLogger("huizenbot.api")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gedeelde HTTP helper
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def fetch_json(url: str, *, params: dict = None, headers: dict = None) -> dict | list:
|
||||
"""
|
||||
GET request met User-Agent, timeout en Retry-After afhandeling.
|
||||
Raises httpx.HTTPError bij aanhoudende fouten.
|
||||
"""
|
||||
hdrs = {"User-Agent": config.USER_AGENT}
|
||||
if headers:
|
||||
hdrs.update(headers)
|
||||
|
||||
for attempt in range(3):
|
||||
r = httpx.get(url, params=params, headers=hdrs, timeout=15)
|
||||
if r.status_code == 429:
|
||||
wait = int(r.headers.get("Retry-After", 60))
|
||||
log.warning("429 op %s, wacht %ds", url, wait)
|
||||
time.sleep(wait)
|
||||
continue
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
raise RuntimeError(f"Blijvend 429 op {url}")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Bjornd
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_BJORND_BASE = "https://www.bjornd.nl"
|
||||
_BJORND_SKIP = {"rented", "rented_ur"}
|
||||
|
||||
_STATUS_MAP = {
|
||||
"available": "beschikbaar",
|
||||
"under_bid": "onder_bod",
|
||||
"under_option": "onder_bod",
|
||||
"sold": "verkocht",
|
||||
"sold_ur": "verkocht",
|
||||
}
|
||||
|
||||
|
||||
def fetch_bjornd() -> list[RawListing]:
|
||||
data = fetch_json(
|
||||
f"{_BJORND_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 _BJORND_SKIP:
|
||||
continue
|
||||
if item.get('salesPrice')>config.MAX_PRICE:
|
||||
continue
|
||||
|
||||
|
||||
listings.append(RawListing(
|
||||
url=_BJORND_BASE + item["url"],
|
||||
source_makelaar="bjornd",
|
||||
status=_STATUS_MAP.get(item.get("statusOrig", ""), "beschikbaar"),
|
||||
adres=item.get("address") or None,
|
||||
postcode=item.get("zipcode") or None,
|
||||
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=item.get("plotSurface") or None,
|
||||
kamers=item.get("rooms") or None,
|
||||
slaapkamers=item.get("bedrooms") or None,
|
||||
hero_image_url=item.get("photo") or None,
|
||||
extra=json.dumps({
|
||||
"balcony": item.get("balcony"),
|
||||
"garden": item.get("garden"),
|
||||
"mainType": item.get("mainType"),
|
||||
"buildType": item.get("buildType"),
|
||||
"district": item.get("district"),
|
||||
"lat": item.get("lat"),
|
||||
"lng": item.get("lng"),
|
||||
"isFurnished": item.get("isFurnished"),
|
||||
"hasOpenHouse": item.get("hasOpenHouse"),
|
||||
"description": item.get("description"),
|
||||
"photos": item.get("photos"),
|
||||
}, ensure_ascii=False),
|
||||
))
|
||||
|
||||
log.info("bjornd: %d koopwoningen opgehaald", len(listings))
|
||||
return listings
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SCRAPERS — exporteer hier alle actieve API adapters
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SCRAPERS = {
|
||||
'bjornd': fetch_bjornd,
|
||||
}
|
||||
Reference in New Issue
Block a user