fix: KoePi filtert Angebote anderer Filialen derselben Kette anhand der echten Adresse raus
This commit is contained in:
@@ -41,6 +41,29 @@ function getAddress(retailer) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Straßennamen robust vergleichbar machen (Groß-/Kleinschreibung, "straße"/"str.",
|
||||
// Leerzeichen/Punkte egal — Hausnummer bleibt Teil des Vergleichs)
|
||||
function normalizeAddress(s) {
|
||||
return (s || '')
|
||||
.toLowerCase()
|
||||
.replace(/straße|strasse/g, 'str')
|
||||
.replace(/[^a-zäöüß0-9]/g, '');
|
||||
}
|
||||
|
||||
// Prüft, ob die tatsächlich gescrapte Adresse zu einer der bekannten LOKALEN
|
||||
// Filialen für diesen Händler passt. Ohne erkannten Händler oder ohne gescrapte
|
||||
// Adresse wird NICHT gefiltert (lieber ein Angebot zuviel zeigen als eins zu
|
||||
// Unrecht verstecken, falls marktguru das Layout mal ändert).
|
||||
function isLocalBranch(retailer, realAddress) {
|
||||
const n = (retailer || '').toLowerCase();
|
||||
const key = Object.keys(RETAILER_ADDRESSES).find(k => n.includes(k));
|
||||
if (!key) return true;
|
||||
if (!realAddress) return true;
|
||||
const expectedParts = RETAILER_ADDRESSES[key].split('/').map(p => normalizeAddress(p.trim()));
|
||||
const real = normalizeAddress(realAddress);
|
||||
return expectedParts.some(p => real.includes(p) || (p && p.includes(real)));
|
||||
}
|
||||
|
||||
// ── Angebote via Puppeteer von marktguru ─────────────────────────────────────
|
||||
async function scrapeMarktguru(forceRefresh = false) {
|
||||
const cacheKey = 'koepi:mg';
|
||||
@@ -104,7 +127,10 @@ async function scrapeMarktguru(forceRefresh = false) {
|
||||
const img = card.querySelector('img.offer-list-item-img');
|
||||
const badge = card.querySelector('.badge, .discount-badge')?.textContent?.trim() || '';
|
||||
const imgSrc = img?.src?.replace('/medium.webp', '/large.webp') || img?.src || null;
|
||||
return { name, brand, price, oldPrice, retailer, dateRange, validity, description, badge, address: '', image: imgSrc };
|
||||
const retailerDd = card.querySelector('dd.retailer-name');
|
||||
const addressDd = retailerDd?.nextElementSibling?.tagName === 'DD' ? retailerDd.nextElementSibling : null;
|
||||
const realAddress = addressDd?.textContent?.trim() || '';
|
||||
return { name, brand, price, oldPrice, retailer, dateRange, validity, description, badge, address: '', realAddress, image: imgSrc };
|
||||
});
|
||||
});
|
||||
|
||||
@@ -138,8 +164,14 @@ async function scrapeMarktguru(forceRefresh = false) {
|
||||
return hasKP && isTargetRetailer(o.retailer);
|
||||
});
|
||||
|
||||
// Nur Angebote der tatsächlich bekannten, lokalen Filialen behalten (marktguru
|
||||
// zeigt teils auch weiter entfernte Filialen derselben Kette, z.B. "REWE Dortmund"
|
||||
// oder einen anderen Netto/Kaufland-Standort — anhand der echten gescrapten
|
||||
// Adresse rausfiltern statt sie fälschlich mit der lokalen Adresse zu überschreiben)
|
||||
const localOnly = filtered.filter(o => isLocalBranch(o.retailer, o.realAddress));
|
||||
|
||||
// Händler normalisieren: E center = EDEKA
|
||||
for (const o of filtered) {
|
||||
for (const o of localOnly) {
|
||||
if (o.retailer?.toLowerCase().includes('e center')) o.retailerDisplay = o.retailer;
|
||||
else o.retailerDisplay = o.retailer;
|
||||
}
|
||||
@@ -147,7 +179,7 @@ async function scrapeMarktguru(forceRefresh = false) {
|
||||
// Deduplizieren: gleicher normalisierter Händler + gleicher Preis → ein Eintrag
|
||||
// Kürzeste Beschreibung nehmen (sauberste)
|
||||
const merged = new Map();
|
||||
for (const o of filtered) {
|
||||
for (const o of localOnly) {
|
||||
const normalizedRetailer = o.retailer?.toLowerCase().replace('e center','edeka') || '';
|
||||
const key = `${normalizedRetailer}|${o.price}`;
|
||||
if (merged.has(key)) {
|
||||
|
||||
Reference in New Issue
Block a user