v5: Mollie (EU) statt Stripe, Sitemap + llms-full.txt, /prognose-Methodik, ausgebauter Datenschutz/Transparenz, Scraper-Adapter toom/bauhaus/hornbach + compose/cron-ready, checkedAt-Stempel im Monitor

This commit is contained in:
2026-06-28 07:26:21 +00:00
parent 7636a00d9f
commit a266b14482
18 changed files with 265 additions and 11 deletions
+18
View File
@@ -0,0 +1,18 @@
// Bauhaus-Adapter (Muster wie OBI: Produktseite → Preis via JSON-LD + Markt-Status-Heuristik).
// TODO: products{} mit echten Produkt-URLs/Artikelnummern füllen; Per-Markt-Endpoint ergänzen.
const UA = 'KuehlfinderBot/0.1 (+https://kuehlfinder.de/bot)';
export const meta = { id: 'bauhaus', name: 'Bauhaus', kind: 'baumarkt' };
export const products = { /* 'portasplit': 'https://www....' */ };
function jsonld(html){const o=[];const re=/<script[^>]+application\/ld\+json[^>]*>([\s\S]*?)<\/script>/gi;let m;while((m=re.exec(html))){try{o.push(JSON.parse(m[1]));}catch{}}return o;}
function offer(ld){for(const n of (Array.isArray(ld)?ld:[ld])){const g=n['@graph']||[n];for(const x of g){if(x&&(x['@type']==='Product'||x.offers)){const of=Array.isArray(x.offers)?x.offers[0]:x.offers;if(of)return{price:Number(of.price??of.lowPrice),availability:String(of.availability||'')};}}}return null;}
export async function fetchProduct(productId,{marketId}={}){
const url=products[productId]; if(!url) return null;
const res=await fetch(url,{headers:{'User-Agent':UA,'Accept-Language':'de-DE'}}); if(!res.ok) throw new Error('Bauhaus '+res.status);
const html=await res.text(); const o=jsonld(html).map(offer).find(Boolean);
const inStore=/vorrätig|verfügbar/i.test(html)&&!/nicht (vorrätig|verfügbar)/i.test(html);
const price=o?.price ?? Number((html.match(/(\d{2,4})[,.](\d{2})\s*€/)||[])[0]?.replace(/[^\d,]/g,'').replace(',','.'));
return { productId, source:'bauhaus', marketId:marketId??null, price: price||null,
availability: inStore?'in_stock':(o&&/InStock/i.test(o.availability)?'online_available':'out_of_stock'),
checkedAt:new Date().toISOString(), url };
}
export async function fetchStores(){ return []; }