Skip to content

Instantly share code, notes, and snippets.

@3110
Last active May 31, 2026 14:27
Show Gist options
  • Select an option

  • Save 3110/78ee3c08cc929f07e1e10e99f919cdc4 to your computer and use it in GitHub Desktop.

Select an option

Save 3110/78ee3c08cc929f07e1e10e99f919cdc4 to your computer and use it in GitHub Desktop.
Amazon.co.jpのURLを短くするTampermonkey用UserScript
// ==UserScript==
// @name Amazon URL Shortener JP (ISBN-10 first, else 13->10, else ASIN)
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Shorten Amazon.co.jp URLs to /dp/ISBN-10 (books) or /dp/ASIN (others). If only ISBN-13 exists and starts with 978, convert to ISBN-10.
// @match https://www.amazon.co.jp/*
// @grant none
// ==/UserScript==
(() => {
/** utils **/
const onlyDigitsX = s => s.replace(/[^\dXx]/g, '');
const isASIN = s => /^[A-Z0-9]{10}$/.test(s);
const isISBN10 = s => /^\d{9}[\dX]$/i.test(s);
const isISBN13 = s => /^(97[89])\d{10}$/.test(s);
// ISBN-13 (978xxxxxxxxxxx) -> ISBN-10(※979は変換不可)
const isbn13to10 = (isbn13) => {
const d = isbn13.replace(/\D/g, '');
if (!/^978\d{10}$/.test(d)) return null; // 978以外は不可
const core9 = d.slice(3, 12); // 12桁中、出版社+書名の9桁
// ISBN-10 チェックデジット計算
let sum = 0;
for (let i = 0; i < 9; i++) {
sum += (10 - i) * parseInt(core9[i], 10);
}
const mod = sum % 11;
const check = (11 - mod) % 11;
const cd = (check === 10) ? 'X' : String(check);
return core9 + cd;
};
// URL から ASIN を拾う
const getASINFromUrl = (url) => (
url.match(/\/dp\/([A-Z0-9]{10})(?:[/?#]|$)/)?.[1] ||
url.match(/\/gp\/product\/([A-Z0-9]{10})(?:[/?#]|$)/)?.[1] ||
url.match(/\/exec\/obidos\/ASIN\/([A-Z0-9]{10})(?:[/?#]|$)/)?.[1] ||
url.match(/\/([A-Z0-9]{10})(?:[/?#]|$)/)?.[1] ||
null
);
// DOM から ASIN(保険)
const getASINFromDom = () => {
const el = document.querySelector('[data-asin]');
const v = el?.getAttribute('data-asin');
return (v && isASIN(v)) ? v : null;
};
// 詳細欄テキスト収集
const collectDetailText = () => {
const selectors = [
'#detailBullets_feature_div',
'#detailBulletsWrapper_feature_div',
'#detailBulletsWrapper',
'#productDetails_detailBullets_sections1',
'#productDetails_techSpec_section_1',
'#productDetails_techSpec_section_2',
'#prodDetails',
'#detailBullets',
'#detailBulletsReverse',
'#productDetails_db_sections',
];
let text = '';
for (const sel of selectors) {
const n = document.querySelector(sel);
if (n) text += '\n' + n.innerText;
}
if (!text.trim()) text = document.body?.innerText || '';
return text;
};
const getISBN10FromDom = () => {
const txt = collectDetailText();
// 明示の「ISBN-10:」優先(ハイフン/空白許容)
const m = txt.match(/ISBN[\s\-]*10[^0-9X]*([0-9Xx\-\s]{10,15})/);
if (m) {
const cand = onlyDigitsX(m[1]).toUpperCase();
if (isISBN10(cand)) return cand;
}
// 補助: 10桁候補の周辺に ISBN 文言がある場合のみ
const generic = txt.match(/\b[0-9]{9}[0-9Xx]\b/g);
if (generic) {
for (const s of generic) {
const idx = txt.indexOf(s);
const around = txt.slice(Math.max(0, idx - 24), idx + s.length + 24);
if (/ISBN/i.test(around)) {
const cand = s.toUpperCase();
if (isISBN10(cand)) return cand;
}
}
}
return null;
};
const getISBN13FromDom = () => {
const txt = collectDetailText();
// 「ISBN-13: 978-...」のような明示表記
const m = txt.match(/ISBN[\s\-]*13[^0-9]*([0-9\-\s]{13,20})/i);
if (m) {
const cand = m[1].replace(/\D/g, '');
if (isISBN13(cand)) return cand;
}
// 広めに 978/979 始まり13桁(ハイフン区切り対応)
const all13 = txt.match(/(97[89][\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d[\s\-]*\d)/g);
if (all13) {
for (const raw of all13) {
const cand = raw.replace(/\D/g, '');
if (isISBN13(cand)) return cand;
}
}
return null;
};
const toShortUrl = (code) => `https://www.amazon.co.jp/dp/${code}`;
const run = () => {
const url = location.href;
// 優先: ISBN-10 -> (無ければ) ISBN-13→10 -> (無ければ) ASIN
let code = null;
const isbn10 = getISBN10FromDom();
if (isbn10) {
code = isbn10;
} else {
const isbn13 = getISBN13FromDom();
const isbn10from13 = isbn13 ? isbn13to10(isbn13) : null;
if (isbn10from13) {
code = isbn10from13;
} else {
const asin = getASINFromUrl(url) || getASINFromDom();
if (asin) code = asin;
}
}
if (!code) {
console.log('[Amazon Shortener] code not found');
return;
}
const shortUrl = toShortUrl(code);
if (url !== shortUrl) {
history.replaceState(null, '', shortUrl);
console.log('[Amazon Shortener] shortened ->', shortUrl);
}
};
// 初回(SPA対策の遅延)
const trigger = () => setTimeout(run, 800);
trigger();
// SPA/遅延ロード対応(デバウンス)
let lastUrl = location.href;
let timer = null;
const onDomChange = () => {
const changed = location.href !== lastUrl;
if (changed) lastUrl = location.href;
clearTimeout(timer);
timer = setTimeout(run, changed ? 800 : 1200);
};
new MutationObserver(onDomChange).observe(document, { subtree: true, childList: true });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment