kindle の本をブクログ形式の csv でエクスポートする@2023初春
Web 版 Kindle のページでコンソールを開く。
コンソールの開き方
-
F12
キーもしくは右クリック →検証
で開発者ツールを開く - 開発者ツールのタブの中から
Console
を選ぶ
以下のコードをコピペして実行する (条件を指定したい場合は後述を参考)
/**
* @param {} books
* @param {'読みたい' | 'いま読んでる' | '読み終わった' | '積読 | ''} defaultStatus
*/
async function exportBooklogCSV(books, defaultStatus = '') {
/**
* @param {[string, string, string, string, string, string, string, string, string, string][]} csv2dArray
* @returns {string}
*/
function csv2dArrayToText(csv2dArray) {
return csv2dArray.map((csvArray) => csvArray.join(',')).join('\r\n');
}
/**
* @param {string} csvText
* @returns {void}
*/
function download(csvText) {
const downloadUrl = URL.createObjectURL(
new Blob([csvText], {
type: 'text/csv',
})
);
const downloadLink = document.createElement('a');
downloadLink.href = downloadUrl;
downloadLink.download = 'booklog.csv';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
function convertBooksDataToCSV2dArray(books) {
return books.map((book) => [
/* ServiceID */ '1', //Amazon.co.jp ServiceID is 1
/* ItemID */ book.asin,
/* ISBN13 */ '',
/* Category */ '-',
/* Evaluation */ '',
/* Status */ '',
/* Review */ '',
/* Tag */ '',
/* Memo */ '',
/* RegistDate */ '',
/* FinishDate */ ''
]);
}
download(csv2dArrayToText(convertBooksDataToCSV2dArray(books)));
}
/**
* @param {'EBOOK' | 'COMICS' | undefined} resourceType
* @param {'acquisition_asc' | 'acquisition_desc' | 'title' | 'author' | 'recency'}
*/
async function fetchBooksViaKindleWeb({ resourceType, sortType } = {}) {
/**
* @param {number} paginationToken
*/
function buildUrl(paginationToken = 0) {
// @note MAX query size seems to be 50
return `https://read.amazon.co.jp/kindle-library/search?query=${
resourceType ? `&resourceType=${resourceType}` : ''
}${paginationToken ? `&paginationToken=${paginationToken}` : ''}&sortType=${
sortType ?? 'acquisition_asc'
}&querySize=50`;
}
/**
* @param {number} pagenationToken
* @returns {Promise<{
* asin: string,
* authors: string[],
* mangaOrComicAsin: boolean,
* originType: string,
* percentageRead: number,
* productUrl: string,
* resourceType: string,
* title: string,
* webReaderUrl: string
* }>} books
*/
async function fetchBooks(paginationToken) {
const res = await fetch(buildUrl(paginationToken)).then((res) =>
res.json()
);
if (res.paginationToken) {
return [...res.itemsList, ...(await fetchBooks(res.paginationToken))];
} else {
return res.itemsList;
}
}
return fetchBooks();
}
fetchBooksViaKindleWeb().then((books) => exportBooklogCSV(books));
マンガ/マンガ以外に絞る場合や取得順の変更をしたい場合は、最後の行を以下のオプションが指定可能である。
kindle からの取得で指定可能なオプションは、本の種類 resourceType: 'EBOOK' or 'COMICS'
と並び順 sortType: 'acquisition_asc' | 'acquisition_desc' | 'title' | 'author' | 'recency'
CSV 変換で指定可能なオプションは、読書ステータスの初期値 '読みたい' | 'いま読んでる' | '読み終わった' | '積読 | '
※デフォルトの asc だと CSV 上は古いものが上に来るが、ブクログは上の行から古いものとして登録するため、時系列順に登録したい場合はデフォルトの asc を推奨する
例えばマンガのみを作者順で、全て読み終わった状態で出力するには以下のように書き換える。
fetchBooksViaKindleCloudReader({ resourceType: 'COMICS', sortType: 'author' }).then((books) => exportBooklogCSV(books, '読み終わった'));
出力される CSV は UTF-8 エンコードなので、 読書ステータスを指定する場合は SHIFT_JIS に変換する。
SHIFT_JIS への変換
- メモ帳でダウンロードした csv ファイルを開く
-
名前を付けて保存
を選択し、画面右下のエンコーディング
からANSI
を選択し保存する
解説
本コードは Kindle から本一覧を取得する fetchBooksViaKindleWeb
と ブクログ向け CSV を出力する exportBooklogCSV
とこれらの実行部から成る。
Kindle Web のページで開発者ツールの Network
タブを見ると表示時に通信していないように見えるが、並び順の切り替えで API 通信が発生する。このエンドポイントは規則通りに叩くことですべての本を取得できるようである。
はじめは paginationToken
を指定せずに叩き、レスポンスに含まれる paginationToken
を次のリクエストで指定し再帰的にリクエストすることで全件取得している。
exportBooklogCSV では本当は ShiftJIS で出力したかったが、現在ブラウザ標準の TextEncoder / TextDecoder では ShiftJIS -> UTF-8 の変換のみで逆はできず、開発者ツールのコンソール内で import
により使用可能な ESM に対応した Encoding のライブラリも現状なさそうなので諦めることにした。
fetchBooksViaKindleWeb
を独立した関数にしているので、ブクログへの export 以外にも利用できる。
リポジトリ
MIT License なので、この方法が動作しなくなったが筆者が更新していない場合、 fork して改変を加えることが可能である。
Discussion