英単語リストからAnkiノートを一括作成する
javascript/typescript
, HTML
, css
を使用して、英単語リストから Anki ノートを一括作成する方法を紹介します。
まず公式サイトのマニュアルを一読することをおすすめします。これも Immersion の一環です。
カードとノートの違い
Anki のノートとカードの概念がややこしいのでまとめました。
- ノートはフィールドとカード(s)を定義する
- カードはあるノートの表示方法を
HTML
,css
などによって定義する
単語リストからノートを作る
txt
形式の単語リストからcsv
形式のノートリストを作ります。
まず単語リストを用意します。
頻出単語 2800+ 語 NGSL
TOEIC 1200+ 語 TSL
Business Words 1700+ 語 BSL
Academic Words 1000+ 語 NAWL
分からなかった単語を weblio 英単語帳に登録してまとめてダウンロード
txt
形式などで保存しておきます。\n
を区切り文字としました。
words.txttext
testhello
Anki からノートを定義します。名前は例えばanki_words
、フィールドは以下のように定義しました。
定義したフィールドに合わせてタイプを定義します。
src/types.tstypescript
export type Note = {Expression: string;IPA: string;Image: string;Video: string;Audio: string;Definition: string;DefinitionJP: string;Difficulty: string;Title: string;URL: string;Origin: string;Date: string;};
用意した単語リストから csv ファイルを出力します。以下の例では Deno を使用しています。Anki は HTML
によってカードを表示するので、innerHTML
などのプロパティでそのまま HTML コードを取得すると後で便利です。以下のコードでは辞書ソースに Cambridge Dictionary と Weblio 英和辞典 を使用しました。画像ソースは Google 画像検索を使用しました。今回は省略しますが、例えばVoiceTubeや YouGlishでその単語が使われている動画や英文を検索してノートを作成しても良いと思います。
src/main.tstypescript
import { writeCSVObjects } from 'https://deno.land/x/csv@v0.7.5/mod.ts';import { Cambridge } from './Cambridge.ts';import { GoogleImages } from './GoogleImages.ts';import { Note } from './types.ts';import { Weblio } from './Weblio.ts';const word_list = Deno.readTextFileSync('./words.txt').split('\n').filter(Boolean);const cambridge = new Cambridge();const weblio = new Weblio();const google = new GoogleImages();const notes: Note[] = [];for (const query of word_list) {try {await new Promise((resolve) => setTimeout(resolve, 500));const entries = await cambridge.find(query);const image = await google.find(query);const jp = await weblio.find(query);const { expression, difficulty, origin, reading } = entries[0];const note: Note = {Expression: expression,IPA: reading,Image: image ?? '',Video: '',Audio: '',Definition: entries.map((note) => note.definitions.join('')).join(''),DefinitionJP: jp ?? '',Difficulty: difficulty,Title: '',URL: '',Origin: origin,Date: new Date().toLocaleString('en-US', {dateStyle: 'medium',timeStyle: 'long',hourCycle: 'h23',}),};notes.push(note);} catch (err) {console.log(err);}}const f = await Deno.open('./notes.csv', { write: true, create: true, truncate: true });const header = Object.keys(notes[0]);await writeCSVObjects(f, notes, { header });f.close();Deno.exit();
細かい実装は省略しますが、例えば以下のコードで Google Images 検索の結果から画像をHTML
形式で取得することが出来ます。
src/GoogleImages.tstypescript
import { JSDOM } from 'https://jspm.dev/jsdom';export class GoogleImages {max_example: number;img_height: number;constructor() {this.max_example = 4;this.img_height = 150;}element(path: string) {return `<img src="${path}" decoding="async" style="height: ${this.img_height}px;">`;}async find(word: string) {const query = encodeURIComponent(word);const url = `https://www.google.com/search?tbm=isch&q="${query}"&safe=active&gl=us&pws=0`;const text = await fetch(url, {headers: {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36',},}).then((res) => res.text());const document: Document = new JSDOM(text).window.document;const images = document.querySelectorAll<HTMLImageElement>('.rg_i');return [...images].map((image) => image.getAttribute('data-src') ?? '').filter(Boolean).slice(0, this.max_example).map((src) => this.element(src)).join('');}}
例えば以下のコマンドで実行します。
zsh
deno run src/main.ts
Anki から csv ファイルをインポートすると以下の画面が表示されます。作成したノートとデッキを選択します。フィールドと各項目を合わせてインポートします。一括作成する場合、同じ単語のカードが既に存在していることがよくあります。その時にインポートしないこともここで設定できます。今回は重複があった場合上書きする設定にしています。フィールドにHTML
を使用している場合、Allow HTML in fields
をチェックします。
あとはカードのテンプレートを作成して完成です。
html
<!-- Front Template --><div id="expression">{{Expression}}</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 id="back_body" style="opacity: 0"><div class="front"><div class="header"><div class="date">{{Date}}</div><div class="title"><a href="{{URL}}">{{Title}}</a></div></div><div id="expression">{{Expression}}</div><div id="audio">{{Audio}}</div></div><hr class="separate" /><div class="panels"><div class="half">{{#Video}}<video src="{{Video}}" controls autoplay></video>{{/Video}}<div class="photos" id="image">{{Image}}</div></div><div class="half" id="word_meaning"><div id="head"><div class=""><span class="" id="expression">{{Expression}}</span><span class="" id="extrainfo">{{Difficulty}}</span></div><div class="head__item"><div id="ipa">{{IPA}}</div><div class="tab"><div class="button tab_links" id="tab_eng" onclick="open_tab(event, 'Eng')">ENG</div><div class="button tab_links" id="tab_jpn" onclick="open_tab(event, 'Jpn')">JPN</div></div></div></div><div id="meaning"><div id="Eng" class="tab_content" style="display: none">{{edit:Definition}}</div><div id="Jpn" class="tab_content" style="display: none">{{edit:DefinitionJP}}</div><div id="origin-wrapper">{{edit:Origin}}</div></div><div id="buttons"><!-- ... --></div></div></div></div><script>// ...</script>
css
は直接カードに定義するか、link
タグでHTML
から読み込み出来ます。別ファイルから読み込む場合、名前の先頭に_
を付ける必要があります。1
css
/* _style.css */#expression {font-size: 2rem;margin: 5px;padding: 5px;font-family: var(--serif);display: inline-block;}/* ... */
デモ
寄付のお願い
このブログやプロジェクトを継続するために寄付を頂ければ幸いです。
BTC: 1L5r7dDeJfq56UvPA96WCvQSDCfSUmwjT4
XRP: rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh
Destination Tag: 106945877