#!/usr/bin/env python3 """ DBA.dk søge-tool — henter listings og returnerer JSON. Bruges af dba-scout agenten til at finde billige ting. Brug: python3 dba-search.py "rtx 3090" [--max-price 5000] [--location herning] [--limit 10] """ import sys, re, json, argparse def search_dba(query: str, max_price: int = None, location: str = None, limit: int = 10) -> list[dict]: try: from scrapling import StealthyFetcher except ImportError: print(json.dumps({"error": "scrapling not installed"})) sys.exit(1) # DBA søge-URL (sort by price ascending) url = f"https://www.dba.dk/recommerce/forsale/search?q={query.replace(' ','+')}&sort=PRICE_ASC" if location: # DBA bruger county-filter, vi filtrerer manuelt i stedet pass try: page = StealthyFetcher.fetch(url, stealthy_headers=True) except Exception as e: return [{"error": str(e)}] results = [] # Primær kilde: SEO structured data (JSON-LD) for s in page.css('script[type="application/ld+json"]'): try: raw = s.get().extract() data_str = raw.split('>', 1)[1].rsplit('<', 1)[0] data = json.loads(data_str) items = data.get('mainEntity', {}).get('itemListElement', []) for item in items: listing = item.get('item', item) price_str = listing.get('offers', {}).get('price', '') try: price = int(float(str(price_str))) if price_str else 0 except Exception: price = 0 if max_price and price > max_price: continue entry = { 'title': listing.get('name', ''), 'price': price, 'url': listing.get('url', ''), 'id': listing.get('sku', ''), 'description': (listing.get('description') or '')[:200], 'condition': listing.get('itemCondition', '').replace('https://schema.org/', ''), } # Lokation fra URL ikke mulig via structured data — flag hvis i location if location: combined = (entry['title'] + entry['description']).lower() if location.lower() not in combined: # Hent listing for at tjekke by — skip for hastighed, marker som unknown entry['location'] = 'unknown' else: entry['location'] = location if entry['title']: results.append(entry) if len(results) >= limit: break except Exception: pass return results def format_result(r: dict) -> str: price = f"{r['price']} kr" if r['price'] else "ukendt pris" loc = f" [{r['location']}]" if r.get('location') else "" cond = " (brugt)" if 'Used' in r.get('condition', '') else "" url = r.get('url', '') return f"• {r['title']}{cond} — {price}{loc}\n {url}" def main(): parser = argparse.ArgumentParser(description='Søg på DBA.dk') parser.add_argument('query', help='Søgeord') parser.add_argument('--max-price', type=int, default=None, help='Max pris i kr') parser.add_argument('--location', default=None, help='By/lokation') parser.add_argument('--limit', type=int, default=10, help='Max antal resultater') parser.add_argument('--json', action='store_true', help='Output som JSON') args = parser.parse_args() results = search_dba(args.query, args.max_price, args.location, args.limit) if args.json or not sys.stdout.isatty(): print(json.dumps(results, ensure_ascii=False, indent=2)) else: if not results: print(f"Ingen resultater for '{args.query}'") else: print(f"\nFandt {len(results)} resultater for '{args.query}':\n") for r in results: print(format_result(r)) print() if __name__ == '__main__': main()