golang
Evernote

Evernote がエクスポートする xml ファイルをプレーンテキストに変換する

表題の雑スクリプトを書いたので誰か欲しい人いるかなという感じで書いておきます。

背景

  • Evernote はノートのエクスポート形式が HTML か、独自の XML フォーマットだけであり、プレーンテキストは選べません。
  • また複数ノートを一括でエクスポートした場合、全ノートが1つの XML に結合されてエクスポートされます。
  • 非常に扱いづらいのでプレーンテキストに変換することにしました。

一応先人の知恵もあるにはありました。以下は PHP での実装です。

panicsteve/enex-dump: PHP script that accepts an Evernote export (ENEX) file and produces a folder of plain text documents.

しかし XML ファイルをまるごとメモリに載せてから処理する形式になっているため、 1000 ノート単位で一括エクスポートして XML が数百MBに達したりしていた自分のユースケースではまともに動かず、自分で作ることにしました。

なおプレーンテキスト変換、ですので、元のノートも文字列データを対象としています。 Evernote は画像や pdf や音声ファイルも収容できますが、それらは考慮外です。

XML のフォーマット

パースするにあたり、 Evernote が出力する XML の構造を簡単に書いておきます。だいたい以下のようになっています。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export3.dtd">
<en-export export-date="20181007T043300Z" application="Evernote" version="Evernote Mac 7.5.2 (457172)">
<note><title>hoge</title><content><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note>
noteの内容
</en-note>
]]></content><created>20150729T142057Z</created><updated>20150729T143036Z</updated><note-attributes><author>example@example.com</author><source>desktop.mac</source><reminder-order>0</reminder-order></note-attributes></note>
<note><title>fuga</title><content><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note>
noteの内容
</en-note>
]]></content><created>20150720T065805Z</created><updated>20150720T070625Z</updated><note-attributes><author> example@example.com</author><source>desktop.mac</source><reminder-order>0</reminder-order></note-attributes></note>
</en-export>
  • 全体は <en-export> タグで囲われている。
  • その中に <note> タグで各ノート情報が入っている。
  • ノート情報の中で、例えば表題は <title> 、ノートの内容は <en-note> といった各タグで囲われている。

基本的に各ノートの情報は

  • <note> 開始タグとノートのタイトル情報で1行
  • DOCTYPE で1行
  • <en-note> の開始タグで1行
  • 内容
  • </en-note> 終了タグで1行
  • </note> 終了タグとファイルメタ情報で1行

と行単位で結構綺麗に分かれていて、これがエクスポートしたノート数分繰り返される形になります。ただし <en-note> 開始終了タグとノートの内容がすべて1行に書かれている場合が例外的にあります(おそらく内容が短いノートの場合?)。

また Evernote のデータは基本的にリッチテキストですので、装飾については HTML タグで付加された状態になっています。

実装

Gist に貼っておきました。

XML をパースしてもよかったのですが、先に貼ったようにだいたい1行ずつ要素が分かれているので、行単位で読み込んでパースして処理してノート1つ分読み込むたびに吐き出して、の方が処理も速そうだったのでそうしました。実装はだいぶ雑ですので動かない場合もあるかもしれません。あと macOS でしか実行してないです。

convert_evernote_xml.go

引数に XML ファイルのパスを与えて実行すると、以下のように動作します。

  • カレントディレクトリに output フォルダが無ければ作成する。
  • 1ノート1ファイルで ouput フォルダ内にエクスポートする。
  • 装飾の HTML タグはすべて破棄する。
  • ファイルの atime と mtime はノートの最終変更時刻にする。