在您安裝前,Greasy Fork镜像希望您了解本腳本包含“可能不受歡迎的功能”,可能幫助腳本的作者獲利,而不能給你帶來任何收益。
作者從這份腳本獲得佣金, 例如透過重寫連結或提供優惠券代碼以加入推薦或附屬代碼
此腳本內的代碼會追蹤您的瀏覽行為。
Compare Amazon prices across different country sites with a leaner, faster script.
// ==UserScript== // @name Amazon Price Checker (FR, DE, ES, IT, BE, NL, UK, COM, PL, SE) by bNj // @namespace http://tampermonkey.net/ // @version 4.02 // @description Compare Amazon prices across different country sites with a leaner, faster script. // @icon https://i.ibb.co/qrjrcVy/amz-price-checker.png // @match https://www.amazon.fr/* // @match https://www.amazon.de/* // @match https://www.amazon.es/* // @match https://www.amazon.it/* // @match https://www.amazon.com.be/* // @match https://www.amazon.nl/* // @match https://www.amazon.co.uk/* // @match https://www.amazon.com/* // @match https://www.amazon.pl/* // @match https://www.amazon.se/* // @grant GM_xmlhttpRequest // @connect amazon.fr // @connect amazon.de // @connect amazon.es // @connect amazon.it // @connect amazon.com.be // @connect amazon.nl // @connect amazon.pl // @connect amazon.se // @connect amazon.co.uk // @connect amazon.com // @connect summarizer.mon-bnj.workers.dev // @connect api.frankfurter.app // @connect alisearch.bnjnas.synology.me // @license All Rights Reserved // @antifeature referral-link // @antifeature tracking // ==/UserScript== (function(){ 'use strict'; const ASIN_RE = /\/([A-Z0-9]{10})(?:[/?]|$)/, PARTNER_IDS = { fr:'bnjmazon-21', es:'bnjmazon08-21', it:'bnjmazon0d-21', de:'geeksince190d-21', 'com.be':'geeksince1900', nl:'bnjmazon-21', pl:'bnjmazon-20', se:'bnjmazon-se-21', 'co.uk':'bnjmazon-UK-21', com:'bnjmazon-20' }, sites = [ {name:'Amazon.fr', c:'fr', f:'https://flagcdn.com/w20/fr.png', cur:'EUR'}, {name:'Amazon.es', c:'es', f:'https://flagcdn.com/w20/es.png', cur:'EUR'}, {name:'Amazon.it', c:'it', f:'https://flagcdn.com/w20/it.png', cur:'EUR'}, {name:'Amazon.de', c:'de', f:'https://flagcdn.com/w20/de.png', cur:'EUR'}, {name:'Amazon.be', c:'com.be', f:'https://flagcdn.com/w20/be.png', cur:'EUR'}, {name:'Amazon.nl', c:'nl', f:'https://flagcdn.com/w20/nl.png', cur:'EUR'}, {name:'Amazon.pl', c:'pl', f:'https://flagcdn.com/w20/pl.png', cur:'PLN'}, {name:'Amazon.se', c:'se', f:'https://flagcdn.com/w20/se.png', cur:'SEK'}, {name:'Amazon.co.uk', c:'co.uk', f:'https://flagcdn.com/w20/gb.png', cur:'GBP'}, {name:'Amazon.com', c:'com', f:'https://flagcdn.com/w20/us.png', cur:'USD'} ]; let asin, basePrice, selPeriod = 'all', firstLoaded = false, exRates, tableCont, chartCont, selEl, checks = []; function main() { if (!extractASIN()) return; fetchExRates().then(() => { if (!getBasePrice()) return; injectStyles(); createBaseUI(); fetchPrices(); // ✅ Appelé uniquement après récupération des taux }); } function extractASIN(){ const m = location.href.match(ASIN_RE); if(!m) return false; asin = m[1]; return true; } function getBasePrice(){ basePrice = getPrice(document, getCurrentCountry()); return basePrice !== null; } function injectStyles(){ const css = `:root{--a:#FF9900;--bg:#fff;--font:Arial,sans-serif;--tc:#333;--bc:#ddd} body{font-family:var(--font)!important} #amz-checker-container{background:var(--bg);border:1px solid var(--bc);border-radius:10px;box-shadow:0 2px 6px rgba(0,0,0,0.1);font-size:12px;color:var(--tc);margin:0 auto;display:flex;flex-direction:column} #amz-checker-header{background:var(--a);color:#fff;padding:5px 10px;border-radius:10px 10px 0 0;display:flex;align-items:center;gap:10px} #amz-checker-header img{width:36px;height:36px} #amz-checker-title{font-size:14px;font-weight:bold} .loading-text-gradient{background-clip:text;color:transparent;background-image:linear-gradient(270deg,black 0%,black 20%,var(--a) 50%,black 80%,black 100%);background-size:200% 100%;animation:loadAnim 2s linear infinite} @keyframes loadAnim{0%{background-position:100% 50%}100%{background-position:0 50%}} #loadingMessage{text-align:center;font-weight:bold;font-size:13px;display:flex;flex-direction:column;align-items:center;margin:10px 0} .amz-checker-content{padding:10px;flex:1} #comparison-table{border:1px solid var(--bc);border-radius:8px;overflow:hidden;margin-bottom:15px} .comparison-row{display:flex;justify-content:space-between;padding:5px 10px;border-bottom:1px solid var(--bc);cursor:pointer;transition:background 0.2s} .comparison-row:hover{background:#f5f5f5} .comparison-row.header-row{background:#eee;font-weight:bold;cursor:default} .comparison-row.header-row:hover{background:#eee} .comparison-row:last-child{border-bottom:none} .comparison-row>div{text-align:center;flex:1} .first-col{flex:0 0 120px;text-align:left !important;overflow:hidden} .price-difference-positive{color:#008000} .price-difference-negative{color:#f00} .chart-container{margin-bottom:15px;border:1px solid var(--bc);border-radius:8px;padding:10px;position:relative;min-height:300px;text-align:center} .chart-container .loader{position:absolute;top:50%;left:50%;margin:-24px 0 0 -24px} .chart-controls{display:flex;align-items:center;gap:15px;margin-bottom:10px;flex-wrap:wrap;justify-content:center} .chart-controls .checkbox-container{display:flex;align-items:center;font-size:12px} .chart-controls .checkbox-label{margin-left:4px} .chart-controls select{padding:3px 6px;font-size:12px} .loader{position:relative;width:48px;height:48px;border-radius:50%;display:inline-block;border-top:4px solid #FFF;border-right:4px solid transparent;box-sizing:border-box;animation:rot 1s linear infinite} .loader::after{content:'';box-sizing:border-box;position:absolute;left:0;top:0;width:48px;height:48px;border-radius:50%;border-left:4px solid #FF3D00;border-bottom:4px solid transparent;animation:rot .5s linear infinite reverse} @keyframes rot{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}} .chart-image{max-width:100%;margin-top:10px} .aliexpress-wrapper{margin-bottom:15px} .aliexpress-container{display:flex;align-items:center;justify-content:center;gap:8px;color:#ff5722;font-weight:bold;border:1px solid var(--bc);border-radius:6px;padding:8px 12px;cursor:pointer;transition:background 0.2s} .aliexpress-container:hover{background:#fff8f0} .aliexpress-icon{width:24px} .aliexpress-results{margin-top:10px;display:flex;flex-wrap:wrap;gap:10px;justify-content:space-evenly} .aliexpress-card{border:1px solid var(--bc);border-radius:4px;padding:5px;width:140px;text-align:center;box-shadow:0 2px 4px rgba(0,0,0,0.1);background:#fff} .aliexpress-card img{width:100%;border-radius:4px 4px 0 0} .product-summary-encart{border:1px solid var(--bc);border-radius:8px;padding:10px;background:#f9f9f9;margin-bottom:15px} ._Y3Itc_selected_2-xMA{font-weight:bold!important} #amz-checker-footer{text-align:right;font-size:0.8em;color:#666;background:#fafafa;border-top:1px solid var(--bc);border-radius:0 0 10px 10px;padding:5px 10px} #amz-checker-footer .footer-logo{width:18px;height:18px;vertical-align:middle;margin-right:5px}`; let s = document.createElement('style'); s.type = 'text/css'; s.textContent = css; document.head.appendChild(s); } function createBaseUI(){ let c = document.createElement('div'); c.id = 'amz-checker-container'; c.innerHTML = `<div id="amz-checker-header"><img src="https://i.ibb.co/qrjrcVy/amz-price-checker.png" alt="Logo"/><span id="amz-checker-title">Amazon Price Checker</span></div> <div class="amz-checker-content"><div id="loadingMessage" class="loading-text-gradient">Checking other Amazon sites...</div></div>`; let p = document.querySelector('.priceToPay,#priceblock_ourprice,#priceblock_dealprice,#priceblock_saleprice'); (p ? p.parentNode : document.body).appendChild(c); } function buildFinalUI(){ let cnt = document.querySelector('#amz-checker-container .amz-checker-content'); if(!cnt)return; cnt.innerHTML = ''; addTable(cnt); addChart(cnt); /*addAliExpress(cnt);*/ addProductSummary(cnt); addFooter(); updateChart(); } function addTable(cnt){ let tw = document.createElement('div'); tw.id = 'comparison-table'; tableCont = document.createElement('div'); let hr = document.createElement('div'); hr.className = 'comparison-row header-row'; ['Site','Price (EUR)','Coupon','Delivery','Total','Difference'].forEach((h,i) => hr.appendChild(cell(h,true,i===0 ? 'first-col' : ''))); tableCont.appendChild(hr); tw.appendChild(tableCont); cnt.appendChild(tw); } const cell = (txt, isH, ex) => { let d = document.createElement('div'); d.innerHTML = txt; if(isH) d.style.fontWeight = 'bold'; if(ex) d.classList.add(ex); return d; }; function insertRow({ s, price, del, coupon, cur }){ let tot = price - coupon + del, row = document.createElement('div'); row.className = 'comparison-row'; row.onclick = () => window.open(`https://www.amazon.${s.c}/dp/${asin}?tag=${PARTNER_IDS[s.c]}`, '_blank'); let diff = tot - basePrice, perc = ((diff/basePrice)*100).toFixed(0), dc = diff < 0 ? 'price-difference-positive' : diff > 0 ? 'price-difference-negative' : ''; row.append( cell(`<img src="${s.f}" style="vertical-align:middle;margin-right:5px;width:20px;height:13px;"> ${s.name}`, false, 'first-col'), cell(showPrice(price, cur)), cell(coupon > 0 ? `- €${coupon.toFixed(2)}` : '-'), cell(del > 0 ? `+ €${del.toFixed(2)}` : '-'), cell(showPrice(tot, cur)), cell(diff !== 0 ? `<span class="${dc}">${diff >= 0 ? '+' : ''}€${diff.toFixed(2)} (${perc}%)</span>` : '-') ); let rows = [...tableCont.querySelectorAll('.comparison-row:not(.header-row)')]; let inserted = false; for(let r of rows){ let t = parseFloat(r.children[4].textContent.replace(/[^0-9.,-]/g, '').replace(',', '.')) || 999999; if(tot < t){ tableCont.insertBefore(row, r); inserted = true; break; } } if(!inserted) tableCont.appendChild(row); } function addChart(cnt){ chartCont = document.createElement('div'); chartCont.className = 'chart-container'; let ctrl = document.createElement('div'); ctrl.className = 'chart-controls'; selEl = document.createElement('select'); [['1m','1 Month'], ['3m','3 Months'], ['6m','6 Months'], ['1y','1 Year'], ['all','All']].forEach(([v, l]) => { let o = document.createElement('option'); o.value = v; o.textContent = l; if(v === selPeriod) o.selected = true; selEl.appendChild(o); }); selEl.onchange = () => { selPeriod = selEl.value; updateChart(); }; ctrl.appendChild(selEl); // Trois cases à cocher: Amazon (désactivée), New, Used [['checkboxAmazon','Amazon','amazon', true, true], ['checkboxNew','New','new', false, true], ['checkboxUsed','Used','used', false, false]] .forEach(([id, label, fn, dis, chk]) => { let wrap = document.createElement('div'); wrap.className = 'checkbox-container'; let inp = document.createElement('input'); inp.type = 'checkbox'; inp.id = id; inp.disabled = dis; inp.checked = chk; inp.onchange = updateChart; let lbl = document.createElement('label'); lbl.htmlFor = id; lbl.textContent = label; lbl.className = 'checkbox-label'; wrap.append(inp, lbl); ctrl.appendChild(wrap); checks.push({ inp, fn }); }); chartCont.appendChild(ctrl); let spin = document.createElement('div'); spin.className = 'loader'; let img = document.createElement('img'); img.alt = `Price history for ${asin}`; img.className = 'chart-image'; img.style.display = 'none'; chartCont.append(spin, img); cnt.appendChild(chartCont); } function updateChart(){ if(!chartCont)return; let cc = getCurrentCountry(), url = getChartUrl(cc, asin, selPeriod), spin = chartCont.querySelector('.loader'), img = chartCont.querySelector('.chart-image'); spin.style.display = 'inline-block'; img.style.display = 'none'; img.src = url; img.onload = () => { spin.style.display = 'none'; img.style.display = 'block'; }; img.onerror = () => { spin.style.display = 'none'; img.style.display = 'block'; img.src = 'https://dummyimage.com/600x200/ccc/000&text=Image+Unavailable'; }; } function getChartUrl(cc, a, tp){ let f = checks.filter(c => c.inp.checked).map(c => c.fn).join('-'), base = `https://charts.camelcamelcamel.com/${cc}/${a}/${f}.png?force=1&zero=0&w=600&h=300&desired=false&legend=1&ilt=1&tp=${tp}&fo=0&lang=en`; return `https://camelcamelcamel.mon-bnj.workers.dev/?target=${encodeURIComponent(base)}`; } function addAliExpress(cnt){ let wrap = document.createElement('div'); wrap.className = 'aliexpress-wrapper'; let btn = document.createElement('div'); btn.className = 'aliexpress-container'; btn.innerHTML = `<img src="https://img.icons8.com/color/48/aliexpress.png" class="aliexpress-icon"><span class="aliexpress-text">Check on AliExpress</span>`; btn.onclick = () => { let txt = btn.querySelector('.aliexpress-text'); txt.textContent = 'Loading...'; txt.classList.add('loading-text-gradient'); let imgEl = document.querySelector('#landingImage') || document.querySelector('#imgTagWrapperId img'), imgUrl = imgEl ? imgEl.src : "https://m.media-amazon.com/images/I/71sAMz1x82L.__AC_SX300_SY300_QL70_ML2_.jpg", url = "https://alisearch.bnjnas.synology.me/search?image_url=" + encodeURIComponent(imgUrl); GM_xmlhttpRequest({ method:'GET', url, onload: r => { txt.classList.remove('loading-text-gradient'); txt.textContent = 'Check on AliExpress'; try { displayAliRes(wrap, JSON.parse(r.responseText)); } catch(e){ txt.textContent = 'Error parsing result'; } }, onerror: () => { txt.classList.remove('loading-text-gradient'); txt.textContent = 'Error fetching data'; } }); }; wrap.appendChild(btn); cnt.appendChild(wrap); } function displayAliRes(container, results){ results.sort((a, b) => parsePrice(a.prix) - parsePrice(b.prix)); let resCont = container.querySelector('.aliexpress-results') || document.createElement('div'); resCont.className = 'aliexpress-results'; resCont.innerHTML = ''; results.forEach(item => { let card = document.createElement('div'); card.className = 'aliexpress-card'; let a = document.createElement('a'); a.href = item.lien; a.target = '_blank'; a.style.textDecoration = 'none'; a.style.color = 'inherit'; let img = document.createElement('img'); img.src = item.image; img.alt = item.titre; let title = document.createElement('div'); title.textContent = item.titre; title.style.cssText = "font-size:12px;margin-top:5px;font-weight:bold;height:40px;overflow:hidden"; let price = document.createElement('div'); price.textContent = item.prix; price.style.cssText = "font-size:12px;color:#ff5722;margin-top:5px"; a.append(img, title, price); card.appendChild(a); resCont.appendChild(card); }); if(!container.contains(resCont)) container.appendChild(resCont); } const parsePrice = s => { let n = parseFloat(s.replace(/[^\d.,-]/g, '').replace(',', '.')); return isNaN(n) ? 999999 : n; }; function addProductSummary(cnt){ let sum = document.querySelector('#cr-product-insights-cards'); if(sum){ let clone = sum.cloneNode(true); clone.classList.add('product-summary-encart'); clone.querySelectorAll('i[id^="close-button-"]').forEach(i => i.remove()); cnt.appendChild(clone); addAspectListeners(clone); } } function addAspectListeners(clone){ clone.querySelectorAll('[id^="aspect-button-0-"]').forEach(btn => { btn.onclick = () => { let X = btn.id.split('-')[3], sheet = document.getElementById(`aspect-bottom-sheet-0-${X}`); if(!sheet)return; clone.querySelectorAll('[id^="aspect-bottom-sheet-0-"]').forEach(s => s.style.display = 'none'); sheet.style.display = 'block'; clone.querySelectorAll('[id^="aspect-button-0-"]').forEach(b => b.classList.remove('_Y3Itc_selected_2-xMA')); btn.classList.add('_Y3Itc_selected_2-xMA'); }; }); } let footerDone = false; function addFooter(){ if(footerDone)return; footerDone = true; let cont = document.getElementById('amz-checker-container'); if(!cont)return; let f = document.createElement('div'); f.id = 'amz-checker-footer'; let ver = (typeof GM_info !== 'undefined' && GM_info.script && GM_info.script.version) ? GM_info.script.version : '4.x'; f.innerHTML = `<img src="https://i.ibb.co/qrjrcVy/amz-price-checker.png" class="footer-logo"> Amazon Price Checker v${ver}`; cont.appendChild(f); } function getCurrentCountry(){ let h = location.hostname; if(h.includes('amazon.com') && !h.includes('amazon.com.be') && !h.includes('amazon.co.uk')) return 'com'; if(h.includes('amazon.de')) return 'de'; if(h.includes('amazon.es')) return 'es'; if(h.includes('amazon.it')) return 'it'; if(h.includes('amazon.com.be')) return 'com.be'; if(h.includes('amazon.nl')) return 'nl'; if(h.includes('amazon.pl')) return 'pl'; if(h.includes('amazon.co.uk')) return 'co.uk'; if(h.includes('amazon.se')) return 'se'; return 'fr'; } function getPrice(doc, ctry) { // Essai standard let el = doc.querySelector('.priceToPay,#priceblock_ourprice,#priceblock_dealprice,#priceblock_saleprice'); if (el) { let rawText = el.textContent.replace(/\u00A0/g, '').replace(/\s/g, ''); let raw = parseFloat(rawText.replace(/[^0-9,\.]/g, '').replace(',', '.')); return raw; } // Cas spécial (Amazon.se, prix éclaté) const wholeEl = doc.querySelector('.a-price-whole'); if (wholeEl) { const tempWhole = wholeEl.cloneNode(true); const decimal = tempWhole.querySelector('.a-price-decimal'); if (decimal) decimal.remove(); const whole = tempWhole.textContent.replace(/[^\d]/g, ''); let frac = "00"; const fracEl = doc.querySelector('.a-price-fraction'); if (fracEl) { frac = fracEl.textContent.replace(/[^\d]/g, ''); } const raw = parseFloat(`${whole}.${frac}`); return raw; } return null; } function getCurrency(ctry) { const s = sites.find(x => x.c.toLowerCase() === ctry.toLowerCase()); return s ? s.cur : 'EUR'; } function toEUR(amt, cur){ if (!exRates || typeof amt !== 'number') return amt; if (cur === 'EUR') return amt; const rate = exRates[cur]; console.log(`[toEUR] ${amt} ${cur} @ ${rate} => ${rate ? amt / rate : amt}`); return rate ? amt / rate : amt; } // Modification de fetchExRates pour forcer la requête si le cache ne contient pas SEK function fetchExRates(){ return new Promise(resolve => { let cached = localStorage.getItem('exchangeRates'), ts = localStorage.getItem('exchangeRatesTimestamp'), now = Date.now(); if(cached && ts && (now - ts < 3600000)){ let storedRates = JSON.parse(cached); if(storedRates["SEK"] !== undefined){ exRates = storedRates; return resolve(); } } GM_xmlhttpRequest({ method:'GET', url:'https://api.frankfurter.app/latest?from=EUR&to=USD,GBP,PLN,SEK', onload: r => { if(r.status === 200){ let data = JSON.parse(r.responseText); exRates = data.rates; console.log('[Frankfurter RAW rates]', data.rates); console.log('[exRates["SEK"]]', data.rates['SEK']); localStorage.setItem('exchangeRates', JSON.stringify(exRates)); localStorage.setItem('exchangeRatesTimestamp', now); } else { exRates = { USD:0.90, GBP:1.15, PLN:4.50, SEK:11.5, EUR:1 }; } resolve(); }, onerror: () => { exRates = { USD:0.90, GBP:1.15, PLN:4.50, SEK:11.5, EUR:1 }; resolve(); } }); }); } function fetchPrices(){ sites.forEach(s => { let url = `https://www.amazon.${s.c}/dp/${asin}?tag=${PARTNER_IDS[s.c]}`; GM_xmlhttpRequest({ method:'GET', url, headers: { 'User-Agent':'Mozilla/5.0','Accept-Language':'en-US,en;q=0.5' }, onload: r => { if (r && r.status === 200) { let doc = new DOMParser().parseFromString(r.responseText, 'text/html'); let p = getPrice(doc, s.c); // toujours en devise locale if (p !== null) { let d = getDelivery(doc); let c = getCoupon(doc, p); // coupon calculé à partir de p (non converti) let convertedPrice = toEUR(p, s.cur); let convertedDelivery = toEUR(d, s.cur); let convertedCoupon = toEUR(c, s.cur); if (!firstLoaded) { firstLoaded = true; buildFinalUI(); } insertRow({ s, price: convertedPrice, del: convertedDelivery, coupon: convertedCoupon, cur: s.cur }); } } }, onerror: () => {} }); }); } function getDelivery(doc){ let m = doc.body.innerHTML.match(/data-csa-c-delivery-price="[^"]*?(\d+[.,]\d{2})/); if(m){ let p = parseFloat(m[1].replace(',','.')); return isNaN(p) ? 0 : p; } return 0; } function getCoupon(doc, curPrice){ let lbl = doc.querySelector('label[id^="couponText"],label[id^="greenBadgepctch"]'); if(!lbl)return 0; let txt = (lbl.textContent || '').replace(/\u00A0/g, ' ').toLowerCase().trim(), cp = 0, m = txt.match(/(\d+(?:[.,]\d+)?)\s*%/); if(m){ let p = parseFloat(m[1].replace(',','.')); if(!isNaN(p) && p > 0 && p < 100) cp = curPrice * (p / 100); } m = txt.match(/(?:€\s*(\d+(?:[.,]\d+)?)|(\d+(?:[.,]\d+))\s*€)/); if(m){ let val = parseFloat((m[1] || m[2] || '').replace(',','.')); if(!isNaN(val) && val > 0 && val <= curPrice) cp = Math.max(cp, val); } return cp; } function showPrice(amt, cur){ if(!exRates || cur === 'EUR') return `€${amt.toFixed(2)}`; return `€${amt.toFixed(2)}<span style="font-size:0.8em; color:#888;" title="Exchange Rate: 1 EUR = ${exRates[cur]} ${cur}">ℹ️</span>`; } main(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址