fix: KöPi - Bild Proxy mit Referer, Adresse, kein Produktuntertitel

This commit is contained in:
2026-07-09 01:09:42 +02:00
parent 0eec03d5c7
commit f3418e51ac
2 changed files with 38 additions and 32 deletions

View File

@@ -66,8 +66,11 @@ async function scrapeMarktguru() {
const descEl = card.querySelector('.info div, .info'); const descEl = card.querySelector('.info div, .info');
const description = descEl?.textContent?.trim() || ''; const description = descEl?.textContent?.trim() || '';
const img = card.querySelector('img.offer-list-item-img'); const img = card.querySelector('img.offer-list-item-img');
const badge = card.querySelector('.badge, .discount-badge, [class*="badge"]')?.textContent?.trim() || ''; const badge = card.querySelector('.badge, .discount-badge')?.textContent?.trim() || '';
return { name, brand, price, oldPrice, retailer, dateRange, validity, description, badge, image: img?.src || null }; const address = card.querySelector('.retailer-address, .address, dd.address, [class*="address"]')?.textContent?.trim() || '';
// Bild: medium → large für bessere Qualität
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 };
}); });
}); });
@@ -175,7 +178,7 @@ router.get('/img', authenticate, (req, res) => {
const parsed = new URL(url); const parsed = new URL(url);
const allowed = ['content-media.bonial.biz','publisher-media.bonial.biz','cdn.marktguru.de','www.marktguru.de']; const allowed = ['content-media.bonial.biz','publisher-media.bonial.biz','cdn.marktguru.de','www.marktguru.de'];
if (!allowed.some(h => parsed.hostname === h)) return res.status(403).end(); if (!allowed.some(h => parsed.hostname === h)) return res.status(403).end();
const r = https.get(url, { headers: {'User-Agent':'Mozilla/5.0'} }, imgRes => { const r = https.get(url, { headers: {'User-Agent':'Mozilla/5.0','Referer':'https://www.marktguru.de/','Origin':'https://www.marktguru.de'} }, imgRes => {
res.setHeader('Content-Type', imgRes.headers['content-type'] || 'image/jpeg'); res.setHeader('Content-Type', imgRes.headers['content-type'] || 'image/jpeg');
res.setHeader('Cache-Control', 'public, max-age=3600'); res.setHeader('Cache-Control', 'public, max-age=3600');
imgRes.pipe(res); imgRes.pipe(res);

View File

@@ -35,42 +35,45 @@ function OfferCard({ offer }) {
display:'flex', flexDirection:'column', display:'flex', flexDirection:'column',
}}> }}>
{/* Produktbild */} {/* Produktbild */}
<div style={{ height:160, background:'#fff', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, overflow:'hidden', position:'relative' }}> {offer.image && (
{offer.image <div style={{ height:160, background:'#f5f5f5', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0, overflow:'hidden', position:'relative' }}>
? <img src={`/api/tools/koepi/img?url=${encodeURIComponent(offer.image)}`} <img
alt={offer.name} style={{ maxHeight:'100%', maxWidth:'100%', objectFit:'contain' }} src={`/api/tools/koepi/img?url=${encodeURIComponent(offer.image)}`}
onError={e => { e.target.style.display='none'; }} alt="König Pilsener"
/> style={{ maxHeight:'100%', maxWidth:'100%', objectFit:'contain' }}
: null onError={e => { e.target.parentElement.style.display='none'; }}
} />
{offer.badge && ( {offer.oldPrice && (
<div style={{ position:'absolute', top:6, left:6, background:'#e2001a', color:'#fff', <div style={{ position:'absolute', top:6, left:6, background:'#e2001a', color:'#fff',
borderRadius:4, padding:'2px 6px', fontSize:9, fontFamily:'monospace', fontWeight:700 }}> borderRadius:4, padding:'2px 6px', fontSize:9, fontFamily:'monospace', fontWeight:700 }}>
{offer.badge} -29%
</div> </div>
)} )}
</div> </div>
)}
<div style={{ padding:'12px 14px', flex:1, display:'flex', flexDirection:'column', gap:5 }}> <div style={{ padding:'12px 14px', flex:1, display:'flex', flexDirection:'column', gap:5 }}>
{/* Händler Badge */} {/* Händler Badge + Adresse */}
<div style={{ <div>
display:'inline-block', alignSelf:'flex-start', <div style={{
background:`${color}22`, border:`1px solid ${color}55`, display:'inline-block',
borderRadius:4, padding:'2px 8px', background:`${color}22`, border:`1px solid ${color}55`,
color, fontFamily:'monospace', fontSize:10, fontWeight:700, borderRadius:4, padding:'2px 8px', marginBottom:3,
}}>{offer.retailer}</div> color, fontFamily:'monospace', fontSize:10, fontWeight:700,
}}>{offer.retailer}</div>
{offer.address && (
<div style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:9 }}>
📍 {offer.address}
</div>
)}
</div>
{/* Name + Marke */} {/* König Pilsener */}
<div style={{ color:'#fff', fontFamily:'monospace', fontSize:13, fontWeight:600, lineHeight:1.3 }}> <div style={{ color:'#fff', fontFamily:'monospace', fontSize:13, fontWeight:600, lineHeight:1.3 }}>
König Pilsener König Pilsener
</div> </div>
{offer.name && offer.name.toLowerCase() !== 'könig pilsener' && (
<div style={{ color:'rgba(255,255,255,0.5)', fontFamily:'monospace', fontSize:11 }}>
{offer.name}
</div>
)}
{/* Beschreibung/Flaschengröße */} {/* Flaschengröße/Beschreibung */}
{offer.description && ( {offer.description && (
<div style={{ color:'rgba(255,255,255,0.55)', fontFamily:'monospace', fontSize:10, lineHeight:1.5 }}> <div style={{ color:'rgba(255,255,255,0.55)', fontFamily:'monospace', fontSize:10, lineHeight:1.5 }}>
{offer.description} {offer.description}