Greasy Fork is available in English.
为亚马逊搜索结果页面上的广告和自然搜索结果添加序号,并为所有广告(含品牌广告)添加广告ID和活动ID,全部显示在商品卡片上。
// ==UserScript==
// @name 亚马逊搜索结果添加序号和广告ID(自用)
// @version 2.3.1
// @description 为亚马逊搜索结果页面上的广告和自然搜索结果添加序号,并为所有广告(含品牌广告)添加广告ID和活动ID,全部显示在商品卡片上。
// @author Haer
// @match https://www.amazon.com/*
// @match https://www.amazon.co.uk/*
// @match https://www.amazon.de/*
// @match https://www.amazon.it/*
// @match https://www.amazon.fr/*
// @match https://www.amazon.es/*
// @match https://www.amazon.se/*
// @match https://www.amazon.com.mx/*
// @match https://www.amazon.co.jp/*
// @match https://www.amazon.ca/*
// @icon https://www.amazon.com/favicon.ico
// @license MIT
// @grant GM_setClipboard
// @run-at document-end
// @namespace http://greasyfork.icu/users/1124651
// ==/UserScript==
(function () {
'use strict';
/************ 多语言 ************/
const translations = {
'zh': { AdID: '广告ID', CampaignID: '活动ID', Copy: '复制', Copied: '已复制' },
'en': { AdID: 'Ad ID', CampaignID: 'Campaign ID', Copy: 'Copy', Copied: 'Copied' },
'de': { AdID: 'Werbe-ID', CampaignID: 'Kampagnen-ID', Copy: 'Kopieren', Copied: 'Kopiert' },
'fr': { AdID: 'ID Publicité', CampaignID: 'ID Campagne', Copy: 'Copier', Copied: 'Copié' },
'es': { AdID: 'ID Anuncio', CampaignID: 'ID Campaña', Copy: 'Copiar', Copied: 'Copiado' },
'it': { AdID: 'ID Annuncio', CampaignID: 'ID Campagna', Copy: 'Copia', Copied: 'Copiato' },
'nl': { AdID: 'Advertentie-ID', CampaignID: 'Campagne-ID', Copy: 'Kopiëren', Copied: 'Gekopieerd' },
'ja': { AdID: '広告ID', CampaignID: 'キャンペーンID', Copy: 'コピー', Copied: 'コピー済み' }
};
function getLanguage() {
const hostname = window.location.hostname;
const countryMap = {
'amazon.co.jp': 'ja',
'amazon.de': 'de',
'amazon.fr': 'fr',
'amazon.es': 'es',
'amazon.it': 'it',
'amazon.co.uk': 'en',
'amazon.ca': 'en',
'amazon.com': 'en',
'amazon.com.mx': 'es',
'amazon.se': 'en'
};
for (const [domain, lang] of Object.entries(countryMap)) {
if (hostname.includes(domain)) return lang;
}
const lang = (navigator.language || navigator.userLanguage || 'en').split('-')[0];
return translations[lang] ? lang : 'en';
}
const lang = getLanguage();
const t = translations[lang];
/************ 工具函数 ************/
function safeJSONParse(str) {
try {
return JSON.parse(str);
} catch (e) {
return null;
}
}
function getUrlParam(url, name) {
try {
const u = new URL(url, location.origin);
return u.searchParams.get(name);
} catch (e) {
return null;
}
}
/************ 复制标签 ************/
function createCopyableLabel(text, title, backgroundColor) {
const container = document.createElement('span');
container.style.display = 'inline-flex';
container.style.alignItems = 'center';
container.style.gap = '4px';
container.style.marginRight = '8px';
container.style.flexWrap = 'nowrap';
const label = document.createElement('span');
label.textContent = text;
label.title = title || text;
label.style.backgroundColor = backgroundColor;
label.style.color = '#fff';
label.style.padding = '2px 8px';
label.style.borderRadius = '4px';
label.style.fontSize = '12px';
label.style.userSelect = 'text';
label.style.webkitUserSelect = 'text';
label.style.whiteSpace = 'nowrap';
label.style.maxWidth = '180px';
label.style.overflow = 'hidden';
label.style.textOverflow = 'ellipsis';
const copyBtn = document.createElement('span');
copyBtn.textContent = '📋';
copyBtn.title = t.Copy;
copyBtn.style.cursor = 'pointer';
copyBtn.style.fontSize = '11px';
copyBtn.style.opacity = '0.8';
copyBtn.style.flexShrink = '0';
const copyText = text.split(': ').pop() || text;
copyBtn.addEventListener('click', (e) => {
e.stopPropagation();
e.preventDefault();
if (typeof GM_setClipboard !== 'undefined') {
GM_setClipboard(copyText, 'text');
} else {
const ta = document.createElement('textarea');
ta.value = copyText;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
try {
document.execCommand('copy');
} catch (err) {}
document.body.removeChild(ta);
}
const old = copyBtn.textContent;
copyBtn.textContent = '✓';
copyBtn.style.color = '#00c853';
setTimeout(() => {
copyBtn.textContent = old;
copyBtn.style.color = '';
}, 1200);
});
container.appendChild(label);
container.appendChild(copyBtn);
return container;
}
function createNumberLabel(text, isAd) {
const label = document.createElement('span');
label.textContent = text;
label.style.backgroundColor = isAd ? '#FF9900' : '#146EB4';
label.style.color = '#fff';
label.style.padding = '2px 8px';
label.style.borderRadius = '4px';
label.style.fontSize = '12px';
label.style.fontWeight = 'bold';
label.style.minWidth = isAd ? '45px' : '32px';
label.style.textAlign = 'center';
label.style.marginRight = '8px';
label.style.flexShrink = '0';
return label;
}
/************ 收集 asin → 广告元数据(SP + multi-ad + 带 asin 的品牌广告) ************/
function collectAsinAdMap() {
const asinToAd = new Map();
// 1)SP 广告:data-s-safe-ajax-modal-trigger
document.querySelectorAll('[data-s-safe-ajax-modal-trigger]').forEach(el => {
const raw = el.getAttribute('data-s-safe-ajax-modal-trigger');
if (!raw) return;
const cfg = safeJSONParse(raw);
if (!cfg || !cfg.ajaxUrl) return;
const pl = getUrlParam(cfg.ajaxUrl, 'pl');
if (!pl) return;
const decoded = decodeURIComponent(pl);
const payload = safeJSONParse(decoded);
if (!payload || !payload.adCreativeMetaData) return;
const details = payload.adCreativeMetaData.adCreativeDetails || [];
details.forEach(d => {
if (d.asin && d.adId && d.campaignId && !asinToAd.has(d.asin)) {
asinToAd.set(d.asin, {
adId: d.adId,
campaignId: d.campaignId
});
}
});
});
// 2)multi-ad:data-multi-ad-feedback-form-trigger
document.querySelectorAll('[data-multi-ad-feedback-form-trigger]').forEach(el => {
const raw = el.getAttribute('data-multi-ad-feedback-form-trigger');
if (!raw) return;
const cfg = safeJSONParse(raw);
if (!cfg || !cfg.multiAdfPayload) return;
const payload = safeJSONParse(cfg.multiAdfPayload);
if (!payload || !payload.adCreativeMetaData) return;
const details = payload.adCreativeMetaData.adCreativeDetails || [];
details.forEach(d => {
if (d.asin && d.adId && d.campaignId && !asinToAd.has(d.asin)) {
asinToAd.set(d.asin, {
adId: d.adId,
campaignId: d.campaignId
});
}
});
});
// 3)带 asin 的 data-ad-feedback(品牌广告):creativeDetails 里如果有 asin 也并入
document.querySelectorAll('[data-ad-feedback]').forEach(el => {
const raw = el.getAttribute('data-ad-feedback');
if (!raw) return;
const payload = safeJSONParse(raw);
if (!payload) return;
const details = payload.creativeDetails || payload.creativeDetail || [];
details.forEach(d => {
const adId = d.adId || d.adID;
const campaignId = d.campaignId || d.campaignID;
const asin = d.asin;
if (!asin || !adId || !campaignId) return;
if (!asinToAd.has(asin)) {
asinToAd.set(asin, {
adId,
campaignId
});
}
});
});
return asinToAd;
}
/************ 判断是否为“显式 Sponsored” ************/
function isAdSearchResultByLabel(result) {
if (!result) return false;
if (result.classList.contains('AdHolder')) return true;
if (result.getAttribute('data-component-type') === 'sp-sponsored-result') return true;
if (result.querySelector('.puis-sponsored-label-text, .s-sponsored-label-text, .s-widget-sponsored-label-text')) return true;
return false;
}
/************ 在商品标题下插入标签 ************/
function insertLabelUnderTitle(result, labelContainer) {
const title =
result.querySelector('h2, h2 a, [data-cy="title-recipe"]') ||
result.querySelector('a.a-link-normal.s-underline-text');
if (title && title.parentElement) {
title.parentElement.insertBefore(labelContainer, title.nextSibling);
} else {
result.insertBefore(labelContainer, result.firstChild);
}
}
/************ 主逻辑:给搜索结果加序号和广告ID ************/
function applyLabelsToResults() {
// 清理旧的
document.querySelectorAll('.amazon-adhelper-label').forEach(el => el.remove());
const asinToAd = collectAsinAdMap();
const results = document.querySelectorAll('[data-component-type="s-search-result"][data-asin]');
let adIndex = 0;
let naturalIndex = 0;
const seenAsinNatural = new Set();
results.forEach(result => {
const asin = result.getAttribute('data-asin');
if (!asin) return;
const meta = asinToAd.get(asin); // 是否在广告反馈里出现过
const hasSponsoredLabel = isAdSearchResultByLabel(result);
// 关键修正:只要这个 ASIN 被识别为广告(出现在广告反馈里),就当广告处理
const isAd = hasSponsoredLabel || !!meta;
const box = document.createElement('div');
box.className = 'amazon-adhelper-label';
box.style.display = 'flex';
box.style.alignItems = 'center';
box.style.flexWrap = 'wrap';
box.style.gap = '6px';
box.style.margin = '4px 0';
if (isAd) {
adIndex++;
box.appendChild(createNumberLabel('AD' + adIndex, true));
if (meta && meta.adId) {
box.appendChild(
createCopyableLabel(`${t.AdID}: ${meta.adId}`, `${t.AdID}: ${meta.adId}`, '#232F3E')
);
}
if (meta && meta.campaignId) {
box.appendChild(
createCopyableLabel(`${t.CampaignID}: ${meta.campaignId}`, `${t.CampaignID}: ${meta.campaignId}`, '#37475A')
);
}
} else {
// 自然位:同一个 ASIN 只记一次自然序号
if (seenAsinNatural.has(asin)) return;
seenAsinNatural.add(asin);
naturalIndex++;
box.appendChild(createNumberLabel('#' + naturalIndex, false));
}
insertLabelUnderTitle(result, box);
});
}
/************ 监听 DOM & URL 变化 ************/
let applyTimer = null;
function scheduleApply() {
clearTimeout(applyTimer);
applyTimer = setTimeout(applyLabelsToResults, 800);
}
window.addEventListener('load', scheduleApply);
document.addEventListener('DOMContentLoaded', scheduleApply);
const domObserver = new MutationObserver(() => {
if (document.querySelector('[data-component-type="s-search-result"]')) {
scheduleApply();
}
});
domObserver.observe(document.documentElement || document.body, {
childList: true,
subtree: true
});
let lastUrl = location.href;
const urlObserver = new MutationObserver(() => {
const current = location.href;
if (current !== lastUrl) {
lastUrl = current;
scheduleApply();
}
});
urlObserver.observe(document.body, { childList: true, subtree: true });
})();