KindleからAnkiノートを一括作成する
SQL
, javascript/typescript
, HTML
, css
を使用して、Kindle で調べた英単語のリストから Anki ノートを一括作成する方法を紹介します。
執筆時に使用した Kindle は Kindle Paperwhite (第 11 世代)、ファームウェアバージョン 5.14.3.2 です。
まず公式サイトのマニュアルを一読することをおすすめします。これも Immersion の一環です。
カードとノートの違い
Anki のノートとカードの概念がややこしいのでまとめました。
- ノートはフィールドとカード(s)を定義する
- カードはあるノートの表示方法を
HTML
,css
などによって定義する
Kindle からノートを作る
Kindle の Vocabulary Builder からノートを作成します。Kindle で本を読んでいるときに単語を検索すると Vocabulary Builder にその単語が保存されていきます。Kindle 内でフラッシュカードを作成する機能がありますが、Anki へインポートしたほうが効率的です。Vocabulary Builder が有効になっているか以下のページで確認できます。
Vocabulary Builder の内容は例えば Mac の場合/Volumes/Kindle/system/vocabulary/vocab.db
に保存されているようです。
以下のコマンドでローカルにコピーします。
zsh
cp /Volumes/Kindle/system/vocabulary/vocab.db vocab.db
以下のクエリで英語の本のタイトルとその本の著者のリストが取得できます。
sql
SELECT title, authors FROM BOOK_INFO WHERE BOOK_INFO.lang = 'en'
以下のクエリで指定した本から、語幹、単語、その単語が使われている英文、タイムスタンプを取得できます。
sql
SELECT WORDS.stem, WORDS.word, LOOKUPS.usage, LOOKUPS.timestampFROM WORDSJOIN LOOKUPS ON WORDS.id = LOOKUPS.word_keyJOIN BOOK_INFO ON BOOK_INFO.id = LOOKUPS.book_keyWHERE BOOK_INFO.title = :title
Anki からノートを定義します。名前は例えばkindle
、フィールドは以下のように定義しました。
定義したフィールドに合わせてタイプを定義します。Video
, Audio
フィールドは必要ないかもしれませんが、例えば難しい単語は調べた動画や音声を後で手動でノートに書き込むことがあるので、ここでは定義しました。
src/types.tstypescript
export type Note = {Expression: string;IPA: string;Image: string;Video: string;Audio: string;Sentence: string;Definition: string;DefinitionJP: string;Difficulty: string;Origin: string;Title: string;Authors: string;Date: string;};
先程用意したリストから csv ファイルを出力します。以下の例では Deno を使用しています。makeNote()
はジェネレーターで各本のメタデータからノートをyield
しています。Anki は HTML によってカードを表示するので、web サイトからinnerHTML
などのプロパティでそのまま HTML コードを取得すると後で便利です。
src/main.tstypescript
import { writeCSVObjects } from 'https://deno.land/x/csv@v0.7.5/mod.ts';import { DB } from 'https://deno.land/x/sqlite@v3.5.0/mod.ts';const db = new DB('vocab.db');let title = '';let authors = '';// console.log(db.query('SELECT title, authors FROM BOOK_INFO WHERE BOOK_INFO.lang = 'en''));// ... get title and authors ...const SQL_LOOKUPS = `SELECT WORDS.stem, WORDS.word, LOOKUPS.usage, LOOKUPS.timestampFROM WORDSJOIN LOOKUPS ON WORDS.id = LOOKUPS.word_keyJOIN BOOK_INFO ON BOOK_INFO.id = LOOKUPS.book_keyWHERE BOOK_INFO.title = :title`;const metadata_list = db.queryEntries<{stem: string;word: string;usage: string;timestamp: number;}>(SQL_LOOKUPS, {title,});const f = await Deno.open('./notes.csv', { write: true, create: true, truncate: true });const header = ['Expression','IPA','Image','Video','Audio','Sentence','Definition','DefinitionJP','Difficulty','Origin','Title','Authors','Date',];await writeCSVObjects(f, makeNote(), { header });f.close();Deno.exit();async function* makeNote() {for (const md of metadata_list) {// ... yield note ...}}
以下のコマンドで実行します。
zsh
deno run src/main.ts
Anki から csv ファイルをインポートすると以下の画面が表示されます。作成したノートとデッキを選択します。フィールドと各項目を合わせてインポートします。一括作成する場合、同じ単語のカードが既に存在していることがよくあります。その時にインポートしないこともここで設定できます。今回は重複があってもインポートする設定にしています。フィールドにHTML
を使用している場合、Allow HTML in fields
をチェックします。
あとはカードのテンプレートを作成して完成です。
html
<!-- Front Template --><div id="front_body" style="opacity: 0"><div class="front"><div class="header"><div class="date">{{Date}}</div><div class="title">{{Title}} - {{Authors}}</div></div><div class="sentence">{{edit:Sentence}}</div><div id="audio">{{Audio}}</div></div><hr class="separate" /></div><script>// ...</script>
html
<!-- Back Template --><link href="_style.css" rel="stylesheet" /><link href="_weblio.css" rel="stylesheet" /><link href="_cambridge.css" rel="stylesheet" /><!-- ... --><div class="front"><div class="header"><div class="date">{{Date}}</div><div class="title">{{Title}} - {{Authors}}</div></div><div class="sentence">{{edit:Sentence}}</div><div id="audio">{{Audio}}</div></div><hr class="separate" /><div class="panels"><div class="half"><div class="photos" id="image">{{Image}}</div></div><div class="half"><div id="definition">{{Definition}}</div><div id="example">{{Example}}</div><div id="URL"><a href="{{URL}}">{{URL}}</a></div><!-- ... --></div></div><!-- ... --><script>// ...</script>
css
は直接カードに定義するか、link
タグでHTML
から読み込み出来ます。別ファイルから読み込む場合、名前の先頭に_
を付ける必要があります。1
css
/* _style.css */.front {font-size: 2rem !important;text-align: center;}#definition,#example,#URL {margin: 0.5rem !important;border: 1px solid;border-color: var(--border-color);border-radius: 5px;padding: 1rem;}ul {padding-left: 1rem;}strong {text-decoration: underline;}/* ... */
デモ
寄付のお願い
このブログやプロジェクトを継続するために寄付を頂ければ幸いです。
BTC: 1L5r7dDeJfq56UvPA96WCvQSDCfSUmwjT4
XRP: rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh
Destination Tag: 106945877