Scrape used-car listings from polovniautomobili.com — Serbia's #1 car marketplace. Returns make, model, year, price (RSD/EUR), mileage, fuel, transmission, engine power, body type, color, seller, location and photos. Enriches each ad from its detail page. Export to JSON or CSV.
Root-cause fix for the 30-day 13% customer success rate.
polovniautomobili.com rewrote its frontend to Next.js; the old
server-rendered <article data-classifiedid> markup, per-ad @type=Car
LD-JSON, dataLayer.push({...}) and the uk-grid spec table are all
gone. search_cars()/search_prices() returned zero rows on every
run, which the scraper correctly treated as fail-loud
(RuntimeError: No listings parsed) — hence the near-total run
failure rate.
Rewrote src/parser.py and src/scraper.py to read the site's
<script id="__NEXT_DATA__"> JSON blob instead: searchResults.results
on the list page (make/model/year/price/mileage/fuel/gearbox/power in
one shot) and productData + contactDetails on the detail page
(full spec sheet, seller, description, photo gallery).
Photo CDN also moved gcdn.polovniautomobili.com ->
cdn.polovniautomobili.com; the new parser builds full-size URLs
from productData.images[].fileName instead of a hostname-pinned
regex, so it no longer breaks on CDN subdomain drift.
Dropped the now-unused selectolax dependency (no more HTML-tree
parsing — everything comes from structured JSON).
Adds tests/fixtures/{search,detail}.html (captured live 2026-07-10)
and tests/test_parser.py — locks in the new __NEXT_DATA__ parsing
against real markup so a future regression fails loud in CI, not in
production.
Adds tests/fixtures/input.qa.json — mandatory cloud-QA fixture,
missing from the previous release.
Bumps to 0.2.0 (pyproject.toml + actor.json) to mark the breaking
parser rewrite.
0.1.1 — 2026-06-10
Force-RESIDENTIAL proxy guard: _resolve_proxy_url now always injects
groups=["RESIDENTIAL"] if apifyProxyGroups is absent, empty, or lacks
RESIDENTIAL, preventing silent fallback to datacenter IPs that 403 on
polovniautomobili.com.
Fail fast with RuntimeError (actionable message) instead of returning
None when proxy is unavailable (plan restriction) or useApifyProxy=False.