こんにちは、まなてぃです。
Cosense(旧: scrapbox)を使い始めて、気軽に読書記録をつけられるようになってきました!

ズボラな私はKindleのハイライトやメモを手動でコピペするのが面倒だったので、楽にコピペできないか考えました。
今回は解決方法として、Kindleで読んだ本のハイライトとメモを簡単に抽出できるブックマークレットを作ってみたので、コードと利用イメージを共有します。
もしよろしければ使ってみてください。
ご注意
- コードの利用は自己責任でお願いいたします。本コードの利用によるトラブル等の責任は負いかねます。
- このコードは素人ノンプログラマーがchatGPTの協力を得て作成したものです。 完璧なものではありません 。
動けばヨシ!の精神で作成しております。 - コードはアレンジしてご活用いただいて問題ありません
- 本コードを販売することはご遠慮ください。
- Kindleの「メモとハイライト」のページの仕様変更が行われた場合、正常にコードが動かなくなる可能性があります。
- コードの詳しい解説については、Claude先生、chatGPT先生に聞いた方が正確な回答が得られると思います。コードのアレンジ等もAIなどに相談してご活用ください。
Cosense(旧: scrapbox)とは
Cosense(旧: Scrapbox)は、ノート共有・情報整理プラットフォームです。 ユーザーが自由に情報を記録し、リンクを張ることで知識を柔軟に管理できるツールとして知られています。
説明するよりも実物を見ていただいた方が早い気がします。
公式ホームページ: https://scrapbox.io/product
Scrapboxは、従来のような階層(フォルダ)分類ではなく、様々な情報を整理できるまったく新しいツールです。単語をカッコで囲むだけで情報をネットワーク化できるので、無秩序なフォルダに頭を抱えることはありません。また、同時編集、画像やYoutube動画の貼り付けといった多彩な機能があるので、これ1つであらゆる情報を集約できます。
https://corp.helpfeel.com/news/scrapbox-25million
筆者が最近はじめた公開プロジェクトはこんな感じです。
ブックマークレットとは
ブックマークレット(Bookmarklet)とは、ブックマークとして保存されたJavaScriptのコードです。Webページ上でそのブックマークをクリックすると、JavaScriptが実行されます。
以下のYouTube動画の解説がイメージしやすく、わかりやすかったです。リンクさせていただきます。
作成したコード
Kindleの「メモとハイライト」ページのハイライトをScrapbox用に出力するブックマークレットを作った(テストウフ様)のコードを参考にさせていただきました。ありがとうございます。
ブックマーク登録用(1行版)
javascript:(function(){'use strict';function escapeHTML(str){return str.replace(/[&<>'"]/g,tag=>({'&':'&','<':'<','>':'>',"'":''','"':'"'}[tag]));}const base=document.getElementById("annotation-scroller");if(!base){alert("このページでは実行できません。Kindleのハイライトページを開いてください。");return;}function extractBookInfo(){let title="タイトル不明";let author="著者不明";const h3Metadata=document.querySelector("h3.kp-notebook-metadata");if(h3Metadata&&h3Metadata.innerText.trim()){title=h3Metadata.innerText.trim();}if(title==="タイトル不明"){const metadata=Array.from(document.querySelectorAll("p.kp-notebook-metadata"));if(metadata.length>=2){title=metadata[0]?.innerText.trim()||title;author=metadata[1]?.innerText.trim()||author;}}if(title==="タイトル不明"){const printable=document.querySelector(".kp-notebook-printable");if(printable){const textNodes=Array.from(printable.childNodes).filter(node=>node.nodeType===Node.TEXT_NODE&&node.textContent.trim()).map(node=>node.textContent.trim());if(textNodes.length>0){title=textNodes[0];}}}if(title==="タイトル不明"){const pageTitle=document.title;if(pageTitle&&pageTitle!=="Your Highlights"){const parts=pageTitle.split(" - ");if(parts.length>=2){title=parts[0].trim();if(author==="著者不明"){author=parts[1].trim();}}else{title=pageTitle.trim();}}}if(title==="タイトル不明"){const selectors=[".kp-notebook-metadata","[data-testid='book-title']",".a-text-bold","h1","h2","h3",".kp-notebook-title"];for(const selector of selectors){const element=document.querySelector(selector);if(element&&element.innerText&&element.innerText.trim()){title=element.innerText.trim();break;}}}if(author==="著者不明"){const metadata=Array.from(document.querySelectorAll("p.kp-notebook-metadata"));if(metadata.length>=2&&metadata[1]?.innerText.trim()){author=metadata[1].innerText.trim();}else{const authorSelectors=[".kp-notebook-author","[data-testid='book-author']",".a-text-normal","p.kp-notebook-metadata"];for(const selector of authorSelectors){const elements=document.querySelectorAll(selector);for(const element of elements){if(element&&element.innerText&&element.innerText.trim()&&element.innerText.trim()!==title){author=element.innerText.trim();break;}}if(author!=="著者不明")break;}}}return{title,author};}function extractHighlights(){const items=base.querySelectorAll("#highlight, #note");return Array.from(items).map(el=>{const text=el.innerText.trim();if(text.length<=2)return null;if(el.id==="note"){return`✏ ${text}`;}else{return`> ${text}`;}}).filter(text=>text!==null).join("\n\n");}const bookInfo=extractBookInfo();const highlights=extractHighlights();console.log("=== Kindleハイライト抽出 デバッグ情報 ===");console.log("取得したタイトル:",bookInfo.title);console.log("取得した著者:",bookInfo.author);console.log("ページタイトル:",document.title);console.log("ハイライト数:",base.querySelectorAll("#highlight").length);console.log("メモ数:",base.querySelectorAll("#note").length);const cover=document.querySelector(".kp-notebook-printable img")?.src||null;const fullText=`${bookInfo.title}\n#${bookInfo.author}\n\n${highlights}`;const resultWindow=window.open("","_blank");const htmlContent=`<html><head><title>Kindleハイライト - ${escapeHTML(bookInfo.title)}</title><style>body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;padding:20px;max-width:800px;margin:auto;line-height:1.6;background-color:#fafafa;}.container{background:white;padding:30px;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}.book-info{margin-bottom:20px;padding-bottom:20px;border-bottom:2px solid #eee;}.book-title{font-size:24px;font-weight:bold;color:#333;margin-bottom:8px;}.book-author{font-size:18px;color:#666;font-style:italic;}.cover-image{max-height:150px;margin:15px 0;border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,0.2);}textarea{width:100%;height:400px;font-size:14px;font-family:'Courier New',monospace;border:2px solid #ddd;border-radius:4px;padding:15px;resize:vertical;}.button-group{margin-bottom:15px;}button{padding:12px 20px;font-size:14px;margin-right:10px;margin-bottom:10px;border:none;border-radius:4px;cursor:pointer;transition:background-color 0.3s;}.copy-button{background-color:#007bff;color:white;}.copy-button:hover{background-color:#0056b3;}.debug-button{background-color:#6c757d;color:white;}.debug-button:hover{background-color:#545b62;}.copied-msg{color:#28a745;font-weight:bold;margin-top:10px;display:none;}.debug-info{background:#f8f9fa;padding:15px;margin-bottom:15px;font-size:12px;border-radius:4px;border-left:4px solid #6c757d;display:none;}.stats{background:#e7f3ff;padding:10px;border-radius:4px;margin-bottom:15px;font-size:14px;}</style></head><body><div class="container"><div class="book-info">${cover?`<img src="${escapeHTML(cover)}" alt="Book Cover" class="cover-image">`:""}<div class="book-title">${escapeHTML(bookInfo.title)}</div><div class="book-author">${escapeHTML(bookInfo.author)}</div></div><div class="stats">📚 ハイライト数: ${base.querySelectorAll("#highlight").length}件 | ✏ メモ数: ${base.querySelectorAll("#note").length}件</div><div class="button-group"><button class="copy-button" onclick="copyToClipboard()">📋 ハイライトをコピー</button><button class="debug-button" onclick="toggleDebugInfo()">🔍 デバッグ情報</button></div><div class="debug-info" id="debug-info"><strong>🔍 デバッグ情報:</strong><br>ページタイトル: ${escapeHTML(document.title)}<br>取得したタイトル: ${escapeHTML(bookInfo.title)}<br>取得した著者: ${escapeHTML(bookInfo.author)}<br>URL: ${escapeHTML(window.location.href)}<br>ハイライト要素数: ${base.querySelectorAll("#highlight").length}<br>メモ要素数: ${base.querySelectorAll("#note").length}</div><div class="copied-msg" id="copied-msg">✅ クリップボードにコピーしました!</div><textarea id="highlights-text" readonly>${escapeHTML(fullText)}</textarea></div><script>function copyToClipboard(){const textarea=document.getElementById('highlights-text');textarea.select();textarea.setSelectionRange(0,99999);navigator.clipboard.writeText(textarea.value).then(()=>{const msg=document.getElementById('copied-msg');msg.style.display='block';setTimeout(()=>msg.style.display='none',3000);}).catch(()=>{document.execCommand('copy');const msg=document.getElementById('copied-msg');msg.style.display='block';setTimeout(()=>msg.style.display='none',3000);});}function toggleDebugInfo(){const debugInfo=document.getElementById('debug-info');debugInfo.style.display=debugInfo.style.display==='none'?'block':'none';}</script></body></html>`;resultWindow.document.write(htmlContent);resultWindow.document.close();})();
開発・デバッグ用(整理された版)
アレンジはClaude先生、chatGPT先生に聞きならがらご自由にどうぞ
クリックして開きます(長いよ)
// ========================================
// Kindleハイライト抽出ブックマークレット
// ========================================
// 開発・デバッグ用(整理された版)
(function() {
'use strict';
// HTMLエスケープ関数
function escapeHTML(str) {
return str.replace(/[&<>'"]/g, tag => ({
'&': '&',
'<': '<',
'>': '>',
"'": ''',
'"': '"'
}[tag]));
}
// ページがKindleハイライトページかチェック
const base = document.getElementById("annotation-scroller");
if (!base) {
alert("このページでは実行できません。Kindleのハイライトページを開いてください。");
return;
}
// 書籍情報を取得する関数
function extractBookInfo() {
let title = "タイトル不明";
let author = "著者不明";
// 方法1: h3.kp-notebook-metadataから取得
const h3Metadata = document.querySelector("h3.kp-notebook-metadata");
if (h3Metadata && h3Metadata.innerText.trim()) {
title = h3Metadata.innerText.trim();
}
// 方法2: p.kp-notebook-metadataから取得
if (title === "タイトル不明") {
const metadata = Array.from(document.querySelectorAll("p.kp-notebook-metadata"));
if (metadata.length >= 2) {
title = metadata[0]?.innerText.trim() || title;
author = metadata[1]?.innerText.trim() || author;
}
}
// 方法3: .kp-notebook-printableのテキストノードから取得
if (title === "タイトル不明") {
const printable = document.querySelector(".kp-notebook-printable");
if (printable) {
const textNodes = Array.from(printable.childNodes)
.filter(node => node.nodeType === Node.TEXT_NODE && node.textContent.trim())
.map(node => node.textContent.trim());
if (textNodes.length > 0) {
title = textNodes[0];
}
}
}
// 方法4: ページタイトルから取得
if (title === "タイトル不明") {
const pageTitle = document.title;
if (pageTitle && pageTitle !== "Your Highlights") {
const parts = pageTitle.split(" - ");
if (parts.length >= 2) {
title = parts[0].trim();
if (author === "著者不明") {
author = parts[1].trim();
}
} else {
title = pageTitle.trim();
}
}
}
// 方法5: 一般的なセレクターで再試行
if (title === "タイトル不明") {
const selectors = [
".kp-notebook-metadata",
"[data-testid='book-title']",
".a-text-bold",
"h1", "h2", "h3",
".kp-notebook-title"
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element && element.innerText && element.innerText.trim()) {
title = element.innerText.trim();
break;
}
}
}
// 著者名の再取得
if (author === "著者不明") {
const metadata = Array.from(document.querySelectorAll("p.kp-notebook-metadata"));
if (metadata.length >= 2 && metadata[1]?.innerText.trim()) {
author = metadata[1].innerText.trim();
} else {
const authorSelectors = [
".kp-notebook-author",
"[data-testid='book-author']",
".a-text-normal",
"p.kp-notebook-metadata"
];
for (const selector of authorSelectors) {
const elements = document.querySelectorAll(selector);
for (const element of elements) {
if (element && element.innerText && element.innerText.trim() &&
element.innerText.trim() !== title) {
author = element.innerText.trim();
break;
}
}
if (author !== "著者不明") break;
}
}
}
return { title, author };
}
// ハイライトを抽出する関数
function extractHighlights() {
const items = base.querySelectorAll("#highlight, #note");
return Array.from(items)
.map(el => {
const text = el.innerText.trim();
if (text.length <= 2) return null; // 空のハイライトを除外
// メモかハイライトかを判定
if (el.id === "note") {
return `✏ > ${text}`;
} else {
return `> ${text}`;
}
})
.filter(text => text !== null) // nullを除外
.join("\n\n"); // 各ハイライトの間に改行を追加
}
// メイン処理
const bookInfo = extractBookInfo();
const highlights = extractHighlights();
// デバッグ情報をコンソールに出力
console.log("=== Kindleハイライト抽出 デバッグ情報 ===");
console.log("取得したタイトル:", bookInfo.title);
console.log("取得した著者:", bookInfo.author);
console.log("ページタイトル:", document.title);
console.log("ハイライト数:", base.querySelectorAll("#highlight").length);
console.log("メモ数:", base.querySelectorAll("#note").length);
// カバー画像を取得
const cover = document.querySelector(".kp-notebook-printable img")?.src || null;
// 最終テキストを作成
const fullText = `【${bookInfo.title}】\n#${bookInfo.author}\n\n${highlights}`;
// 結果表示用の新しいウィンドウを作成
const resultWindow = window.open("", "_blank");
const htmlContent = `
<html>
<head>
<title>Kindleハイライト - ${escapeHTML(bookInfo.title)}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
padding: 20px;
max-width: 800px;
margin: auto;
line-height: 1.6;
background-color: #fafafa;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.book-info {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 2px solid #eee;
}
.book-title {
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.book-author {
font-size: 18px;
color: #666;
font-style: italic;
}
.cover-image {
max-height: 150px;
margin: 15px 0;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
textarea {
width: 100%;
height: 400px;
font-size: 14px;
font-family: 'Courier New', monospace;
border: 2px solid #ddd;
border-radius: 4px;
padding: 15px;
resize: vertical;
}
.button-group {
margin-bottom: 15px;
}
button {
padding: 12px 20px;
font-size: 14px;
margin-right: 10px;
margin-bottom: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.copy-button {
background-color: #007bff;
color: white;
}
.copy-button:hover {
background-color: #0056b3;
}
.debug-button {
background-color: #6c757d;
color: white;
}
.debug-button:hover {
background-color: #545b62;
}
.copied-msg {
color: #28a745;
font-weight: bold;
margin-top: 10px;
display: none;
}
.debug-info {
background: #f8f9fa;
padding: 15px;
margin-bottom: 15px;
font-size: 12px;
border-radius: 4px;
border-left: 4px solid #6c757d;
display: none;
}
.stats {
background: #e7f3ff;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<div class="book-info">
${cover ? `<img src="${escapeHTML(cover)}" alt="Book Cover" class="cover-image">` : ""}
<div class="book-title">${escapeHTML(bookInfo.title)}</div>
<div class="book-author">${escapeHTML(bookInfo.author)}</div>
</div>
<div class="stats">
📚 ハイライト数: ${base.querySelectorAll("#highlight").length}件 |
✏ メモ数: ${base.querySelectorAll("#note").length}件
</div>
<div class="button-group">
<button class="copy-button" onclick="copyToClipboard()">
📋 ハイライトをコピー
</button>
<button class="debug-button" onclick="toggleDebugInfo()">
🔍 デバッグ情報
</button>
</div>
<div class="debug-info" id="debug-info">
<strong>🔍 デバッグ情報:</strong><br>
ページタイトル: ${escapeHTML(document.title)}<br>
取得したタイトル: ${escapeHTML(bookInfo.title)}<br>
取得した著者: ${escapeHTML(bookInfo.author)}<br>
URL: ${escapeHTML(window.location.href)}<br>
ハイライト要素数: ${base.querySelectorAll("#highlight").length}<br>
メモ要素数: ${base.querySelectorAll("#note").length}
</div>
<div class="copied-msg" id="copied-msg">✅ クリップボードにコピーしました!</div>
<textarea id="highlights-text" readonly>${escapeHTML(fullText)}</textarea>
</div>
<script>
function copyToClipboard() {
const textarea = document.getElementById('highlights-text');
textarea.select();
textarea.setSelectionRange(0, 99999);
navigator.clipboard.writeText(textarea.value).then(() => {
const msg = document.getElementById('copied-msg');
msg.style.display = 'block';
setTimeout(() => msg.style.display = 'none', 3000);
}).catch(() => {
// フォールバック: 古いブラウザ対応
document.execCommand('copy');
const msg = document.getElementById('copied-msg');
msg.style.display = 'block';
setTimeout(() => msg.style.display = 'none', 3000);
});
}
function toggleDebugInfo() {
const debugInfo = document.getElementById('debug-info');
debugInfo.style.display = debugInfo.style.display === 'none' ? 'block' : 'none';
}
</script>
</body>
</html>`;
resultWindow.document.write(htmlContent);
resultWindow.document.close();
})();
ブックマークレットの登録方法
ブックマークレットを使用するためには、以下の手順で登録します。今回はGoogleChromeでのやり方を例に書きます。
- Chromeで任意のページを開く
まずはどのページでもよいので、ChromeでWebページを開いておきます。 - ブックマークバーを表示する
Ctrl + Shift + B
(Macの場合はCmd + Shift + B
)でブックマークバーを表示できます。表示されていない場合はこの操作を行ってください。 - ブックマークを追加する
ブックマークバー上で右クリック →「ページを追加」を選択。 - 名前とURLを入力する
- 名前:わかりやすい名前を設定(例:Kindleハイライトとメモを取得するブックマークレット)
- URL:
javascript:
で始まるJavaScriptコード(ブックマーク登録用(1行版))のコードを入力
- 保存する
「保存」ボタンをクリックして完了です。


注意点
ブックマークレットは1行のコードにする必要があります。複数行のコードを書く場合は、すべて1行に圧縮してください(改行やコメントは不可)。
先頭に必ず javascript: がついているか確認してください。これがないと通常のURLとして解釈されてしまうようです。
作成したブックマークレットの使い方
Kindleのメモとハイライトを確認できるページを開きます。
https://read.amazon.co.jp/notebook
メモとハイライトを取得したい本のページを開き、登録したブックマークを、ブックマークバーからクリックします。指定のJavaScriptが実行されます。
コードを実行すると別タブが開いて、そこにハイライト内容が表示されます。
テキストをコピーし、コピーしたテキストをCosenseに貼り付けます。


まとめ
ブックマークレットは、ちょっとした自動化に便利な仕組みだと勉強になりました。
Kindleのメモやハイライトを簡単に抽出できるブックマークレットが、読書好きの方の情報整理が少しでも楽になると幸いです。