您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Compare product prices on Amazon.fr, Amazon.de, Amazon.es, and Amazon.it to find the best deal. Integrates CamelCamelCamel for price history charts.
当前为
// ==UserScript== // @name European Price Checker for Amazon (fr, de, es, it) // @namespace http://tampermonkey.net/ // @version 2.4 // @description Compare product prices on Amazon.fr, Amazon.de, Amazon.es, and Amazon.it to find the best deal. Integrates CamelCamelCamel for price history charts. // @author bNj // @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/* // @grant GM_xmlhttpRequest // @connect amazon.fr // @connect amazon.es // @connect amazon.it // @connect amazon.de // @license MIT // ==/UserScript== (function() { 'use strict'; // Entry point function main() { if (!extractASIN()) return; if (!getBasePrice()) return; injectStyles(); createLoadingContainer(); fetchPricesFromOtherSites(); } const ASIN_REGEX = /\/([A-Z0-9]{10})(?:[/?]|$)/; let asin; let basePrice; let selectedTimePeriod = 'all'; const priceResults = []; let requestCount = 0; const PARTNER_IDS = { fr: 'geeksince1983-21', es: 'geeksince1901-21', it: 'geeksince1903-21', de: 'geeksince190d-21' }; const amazonSites = [ { name: 'Amazon.fr', country: 'fr', flag: 'https://flagcdn.com/w20/fr.png' }, { name: 'Amazon.es', country: 'es', flag: 'https://flagcdn.com/w20/es.png' }, { name: 'Amazon.it', country: 'it', flag: 'https://flagcdn.com/w20/it.png' }, { name: 'Amazon.de', country: 'de', flag: 'https://flagcdn.com/w20/de.png' } ]; function extractASIN() { const asinMatch = window.location.href.match(ASIN_REGEX); if (!asinMatch) return false; asin = asinMatch[1]; return true; } function getBasePrice() { basePrice = getPriceFromDocument(document); return basePrice !== null; } function injectStyles() { const styles = ` #amazonPriceComparisonContainer { margin-top: 20px; padding: 10px; background-color: #f9f9f9; border: 1px solid #ccc; border-radius: 8px; position: relative; font-size: 11px; } .comparison-row { cursor: pointer; display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #ccc; } .comparison-row:hover { background-color: #f1f1f1; } .comparison-row.header-row { border-bottom: 2px solid #000; font-weight: bold; pointer-events: none; } #loadingMessage { text-align: center; font-weight: bold; font-size: 14px; display: flex; flex-direction: column; align-items: center; background-clip: text; -webkit-background-clip: text; color: transparent; background-image: linear-gradient(270deg, black 0%, black 20%, #FF9900 50%, black 80%, black 100%); background-size: 200% 100%; animation: loadingAnimation 2s linear infinite; } @keyframes loadingAnimation { 0% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } .active-button { background-color: #ff9900 !important; } .price-difference-positive { color: green; } .price-difference-negative { color: red; } .controls-container { text-align: center; margin: 10px; display: flex; justify-content: center; align-items: center; gap: 10px; } .aliexpress-container { margin-top: 20px; padding: 5px 10px; border: 1px solid #ccc; border-radius: 8px; text-align: center; max-width: 300px; margin-left: auto; margin-right: auto; cursor: pointer; transition: background-color 0.3s ease; } .aliexpress-container:hover { background-color: #ffe6cc; } .aliexpress-link { text-decoration: none; color: inherit; font-weight: bold; display: flex; align-items: center; justify-content: center; } .aliexpress-icon { width: 24px; margin-right: 8px; } .footer { text-align: right; font-size: 0.7em; color: #666; margin-top: 10px; } .footer-logo { width: 20px; height: 20px; vertical-align: middle; margin-right: 5px; } .control-button { padding: 5px 10px; border: 1px solid #ccc; border-radius: 5px; background-color: #f9f9f9; cursor: pointer; } .control-button.active { background-color: #ff9900 !important; } .checkbox-container { display: flex; align-items: center; flex-direction: column; } .checkbox-label { font-weight: normal; } .chart-container { margin-top: 20px; text-align: center; } .chart-image { max-width: 100%; height: auto; border: 1px solid #ccc; border-radius: 8px; } `; const styleSheet = document.createElement('style'); styleSheet.type = 'text/css'; styleSheet.innerText = styles; document.head.appendChild(styleSheet); } function createLoadingContainer() { const priceElement = document.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice, #priceblock_saleprice'); if (priceElement && priceElement.parentNode) { const container = document.createElement('div'); container.id = 'amazonPriceComparisonContainer'; container.innerHTML = ` <div id="loadingMessage"> <img src="https://i.ibb.co/qrjrcVy/amz-price-checker.png" alt="Loading logo" style="width: 50px; height: 50px; margin-bottom: 10px;"> Checking other Amazon sites... </div>`; priceElement.parentNode.appendChild(container); } } function fetchPricesFromOtherSites() { amazonSites.forEach(site => { const url = `https://www.amazon.${site.country}/dp/${asin}?tag=${PARTNER_IDS[site.country]}`; GM_xmlhttpRequest({ method: 'GET', url: url, headers: { 'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'en-US,en;q=0.5' }, onload: response => handleResponse(site, response), onerror: () => handleResponse(site, null) }); }); } function handleResponse(site, response) { requestCount++; if (response && response.status === 200) { const parser = new DOMParser(); const doc = parser.parseFromString(response.responseText, 'text/html'); const price = getPriceFromDocument(doc); const deliveryPrice = getDeliveryPriceFromDocument(doc); if (price !== null) { priceResults.push({ ...site, price, deliveryPrice }); } } if (requestCount === amazonSites.length) { displayAllResults(); } } function displayAllResults() { const priceContainer = document.querySelector('#amazonPriceComparisonContainer'); if (!priceContainer) return; priceContainer.innerHTML = ''; createComparisonTable(priceContainer); addControls(priceContainer); addCamelCamelCamelChart(priceContainer); addAliExpressLink(priceContainer); addFooter(priceContainer); } function createComparisonTable(priceContainer) { const headerRow = document.createElement('div'); headerRow.className = 'comparison-row header-row'; ['Site', 'Price', 'Delivery', 'Total', 'Difference'].forEach(header => { const headerCell = createCell(header, true); headerRow.appendChild(headerCell); }); priceContainer.appendChild(headerRow); priceResults.sort((a, b) => (a.price + a.deliveryPrice) - (b.price + b.deliveryPrice)); priceResults.forEach(result => { const row = document.createElement('div'); row.className = 'comparison-row'; row.onclick = () => window.open(`https://www.amazon.${result.country}/dp/${asin}?tag=${PARTNER_IDS[result.country]}`, '_blank'); const totalPrice = result.price + (result.deliveryPrice || 0); const difference = totalPrice - basePrice; const differencePercentage = ((difference / basePrice) * 100).toFixed(2); const differenceClass = difference < 0 ? 'price-difference-positive' : difference > 0 ? 'price-difference-negative' : ''; row.append( createCell(`<img src="${result.flag}" alt="${result.name} flag" style="vertical-align: middle; margin-right: 5px;"> ${result.name}`), createCell(`€${result.price.toFixed(2)}`), createCell(result.deliveryPrice ? `<img src="https://img.icons8.com/?size=100&id=12248&format=png&color=000000" width="20"/> €${result.deliveryPrice.toFixed(2)}` : '-'), createCell(`€${totalPrice.toFixed(2)}`), createCell(difference !== 0 ? `<span class="${differenceClass}">${difference >= 0 ? '+' : ''}€${difference.toFixed(2)} (${differencePercentage}%)</span>` : '-') ); priceContainer.appendChild(row); }); } function createCell(content, isHeader = false) { const cell = document.createElement('div'); cell.style.flex = '1'; cell.style.textAlign = 'center'; cell.innerHTML = content; if (isHeader) { cell.style.fontWeight = 'bold'; } return cell; } function addControls(priceContainer) { const controlsContainer = document.createElement('div'); controlsContainer.className = 'controls-container'; const timePeriods = [ { id: 'btn1Month', label: '1 Month', value: '1m' }, { id: 'btn3Months', label: '3 Months', value: '3m' }, { id: 'btn6Months', label: '6 Months', value: '6m' }, { id: 'btn1Year', label: '1 Year', value: '1y' }, { id: 'btnAll', label: 'All', value: 'all' } ]; timePeriods.forEach(period => { const button = document.createElement('button'); button.id = period.id; button.textContent = period.label; button.className = `control-button ${period.value === selectedTimePeriod ? 'active' : ''}`; button.addEventListener('click', () => { selectedTimePeriod = period.value; document.querySelectorAll('.control-button').forEach(btn => { btn.classList.remove('active'); }); button.classList.add('active'); updateChartUrl(); }); controlsContainer.appendChild(button); }); const checkboxes = [ { id: 'checkboxAmazon', label: 'Amazon', filename: 'amazon', disabled: true, checked: true }, { id: 'checkboxNew', label: 'New', filename: 'new', checked: false }, { id: 'checkboxUsed', label: 'Used', filename: 'used', checked: false } ]; checkboxes.forEach(checkbox => { const container = document.createElement('div'); container.className = 'checkbox-container'; const checkboxElement = document.createElement('input'); checkboxElement.type = 'checkbox'; checkboxElement.id = checkbox.id; checkboxElement.checked = checkbox.checked; if (checkbox.disabled) checkboxElement.disabled = true; checkboxElement.addEventListener('change', updateChartUrl); const labelElement = document.createElement('label'); labelElement.htmlFor = checkbox.id; labelElement.textContent = checkbox.label; labelElement.className = 'checkbox-label'; container.append(checkboxElement, labelElement); controlsContainer.appendChild(container); }); priceContainer.appendChild(controlsContainer); } function addCamelCamelCamelChart(priceContainer) { const chartContainer = document.createElement('div'); chartContainer.className = 'chart-container'; const countryCode = getCurrentCountryCode(); const chartUrl = getCamelChartUrl(countryCode, asin, selectedTimePeriod); const camelUrl = `https://${countryCode}.camelcamelcamel.com/product/${asin}`; chartContainer.innerHTML = `<a href="${camelUrl}" target="_blank"><img src="${chartUrl}" alt="Price history for ${asin}" class="chart-image"></a>`; priceContainer.appendChild(chartContainer); } function addAliExpressLink(priceContainer) { const titleElement = document.querySelector('#productTitle'); const productTitle = titleElement ? titleElement.textContent.trim() : null; if (!productTitle) return; // Simplify the product title by removing unnecessary words const simplifiedTitle = simplifyProductTitle(productTitle); const searchUrl = `https://www.aliexpress.com/wholesale?SearchText=${encodeURIComponent(simplifiedTitle)}`; const aliexpressIcon = 'https://cdn.icon-icons.com/icons2/2699/PNG/96/aliexpress_logo_icon_167892.png'; const aliexpressContainer = document.createElement('div'); aliexpressContainer.className = 'aliexpress-container'; aliexpressContainer.innerHTML = ` <a href="${searchUrl}" target="_blank" class="aliexpress-link"> <img src="${aliexpressIcon}" alt="AliExpress Icon" class="aliexpress-icon"> Check this product on AliExpress </a> `; priceContainer.appendChild(aliexpressContainer); } // Function to simplify the product title by removing stop words in multiple languages function simplifyProductTitle(title) { const stopWords = [ // English 'the', 'and', 'or', 'for', 'with', 'a', 'an', 'of', 'in', 'on', 'at', 'by', 'from', 'to', // French 'le', 'la', 'les', 'un', 'une', 'des', 'du', 'de', 'et', 'ou', 'pour', 'avec', 'en', 'sur', 'au', 'aux', 'dans', 'par', 'à', // Spanish 'el', 'la', 'los', 'las', 'un', 'una', 'unos', 'unas', 'de', 'y', 'o', 'para', 'con', 'en', 'sobre', 'al', 'a', 'del', 'por', // Italian 'il', 'la', 'i', 'gli', 'le', 'un', 'una', 'uno', 'dei', 'delle', 'degli', 'di', 'e', 'o', 'per', 'con', 'in', 'su', 'al', 'allo', 'della', 'dell', 'da', 'dal', 'dalla', // German 'der', 'die', 'das', 'und', 'oder', 'für', 'mit', 'ein', 'eine', 'einer', 'eines', 'einem', 'einen', 'den', 'dem', 'des', 'von', 'zu', 'im', 'an', 'auf', 'bei', 'als', 'aus', 'nach', 'bei', 'gegen', 'um', 'durch', 'über', 'unter', 'am', 'vom' ]; const words = title.toLowerCase().split(/\s+/); const filteredWords = words.filter(word => !stopWords.includes(word) && word.length > 1); return filteredWords.join(' '); } function addFooter(container) { const footer = document.createElement('div'); footer.className = 'footer'; footer.innerHTML = ` <img src="https://i.ibb.co/qrjrcVy/amz-price-checker.png" alt="Logo" class="footer-logo"> European Price Checker for Amazon by bNj v${GM_info.script.version} `; container.appendChild(footer); } function getCurrentCountryCode() { const hostname = window.location.hostname; if (hostname.includes('amazon.de')) return 'de'; if (hostname.includes('amazon.es')) return 'es'; if (hostname.includes('amazon.it')) return 'it'; return 'fr'; } function getCamelChartUrl(countryCode, asin, timePeriod) { const selectedFilenames = getSelectedFilenames(); return `https://charts.camelcamelcamel.com/${countryCode}/${asin}/${selectedFilenames}.png?force=1&zero=0&w=600&h=300&desired=false&legend=1&ilt=1&tp=${timePeriod}&fo=0&lang=en`; } function getSelectedFilenames() { const checkboxes = [ { id: 'checkboxAmazon', filename: 'amazon' }, { id: 'checkboxNew', filename: 'new' }, { id: 'checkboxUsed', filename: 'used' } ]; return Array.from(document.querySelectorAll('input[type="checkbox"]:checked')) .map(checkbox => checkboxes.find(cb => cb.id === checkbox.id)?.filename) .filter(Boolean) .join('-'); } function updateChartUrl() { const countryCode = getCurrentCountryCode(); const chartUrl = getCamelChartUrl(countryCode, asin, selectedTimePeriod); const camelUrl = `https://${countryCode}.camelcamelcamel.com/product/${asin}`; const chartImage = document.querySelector('#amazonPriceComparisonContainer img[alt^="Price history"]'); if (chartImage) { chartImage.src = chartUrl; chartImage.parentElement.href = camelUrl; } } function getPriceFromDocument(doc) { const priceElement = doc.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice, #priceblock_saleprice'); if (!priceElement) return null; const priceText = priceElement.textContent; return parsePrice(priceText); } function parsePrice(priceText) { if (!priceText) return null; const cleanedText = priceText.replace(/[^0-9,\.]/g, '').replace(',', '.'); const price = parseFloat(cleanedText); return isNaN(price) ? null : price; } function getDeliveryPriceFromDocument(doc) { const deliveryMatch = doc.body.innerHTML.match(/data-csa-c-delivery-price="[^"]*?(\d+[.,]\d{2})/); if (deliveryMatch) { const priceStr = deliveryMatch[1].replace(',', '.'); const price = parseFloat(priceStr); return isNaN(price) ? 0 : price; } return 0; } // Start the script main(); })();