OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

Evernoteのノートを画像も含めNotionへインポート

年も明けたので、Evernoteを卒業します。

Webクリップにもメモにも使えて、できるだけ無料か買い切りで使えて、すぐにサービス終了してしまわなさそうなものを条件として探すと、移行先はほぼNotion一択になるかなぁと思いました。

https://www.notion.so/

Notionには公式でEvernoteからのインポート機能があるが、Webクリップや画像のインポートには対応していない。

それ自体はまあ諦めがつくものの、致命的なのは、インポート中にそういうインポートできないノートがあると、コンソールにエラーを吐き捨てて静かに処理を中止してしまうこと。
エラーとなったノートの特定も容易ではない。

この時点でちょっとNotion自体に不安も生じるところではあるが、、まあちょっと諦めずに使ってみることにします。

やること

Evernoteのノートブック単位で、ノートをNotionに一括インポートする。

レイアウトの維持までは期待しないが、メモもWebクリップもインポートできて、画像もあわせて取り込まれること。

幸いにも、非公式なインポートツールを作っている人がいたので利用させていただく。
https://github.com/vzhd1701/enex2notion

※試す場合は自己責任でお願いします

実行環境

Python の実行環境が必要。

Python 3.6 以上と書いているが、手元の環境だと 3.6 で実行したら dataclasses のインポートでエラーになったので、3.7 以上でやるほうがいいかも。

※追記:ツール作者の方からコメントいただき、ツールバージョン v0.2.4 で Python 3.6 対応されているとのこと。

事前準備

  • インポート対象のノートブックを enex 形式でエクスポートしておく
    • 現在のEvernoteアプリではこのエクスポートができない?らしいので、その場合は Evernote Legacy を使うか、同じ人が作っているevernote-backupを利用できるらしい
  • Notionの token_v2 を取得しておく

手順

基本的にはツールのREADMEに書いてあるとおりです。

  • enex2notion をインストール
    • pip を使うなら以下のとおり
$ pip install enex2notion
$ enex2notion --version  // 0.2.3
  • token を指定せずに dry run してみる
$ enex2notion --verbose foo.enex
  • エラーにならなければ、token を指定して実行
    • warning っぽいものはいっぱい出たが無視した
$ enex2notion --verbose --token [TOKEN] foo.enex
  • Notion側で Evernote ENEX Import というノートの下に取り込まれていることを確認する

注意点

  • 文章が改行単位でブロックとしてひとつずつ取り込まれるため、長文のメモや、ごちゃごちゃした情報の含まれるWebページのクリップなどは相当な時間がかかる(1ノートで10分以上とか…)
    • 公式のインポート機能で事足りるなら、そちらのほうが圧倒的に速い
  • 使った感じ、ツールの作りはわりと粗めな印象
    • 途中でエラーになると時間がもったいないので、最初に token なしで dry run しておくことをオススメ
  • 手元のノートだと以下のようなエラーになるものがあった
    • Evernote上でファイルを保存し直してから再実行したらエラーにならなくなった
    • ※追記:ツール v0.2.4 で修正されたかも?
ERROR: Unhandled exception while parsing note '[ノート名]'!
Traceback (most recent call last):
  File "path\to\python\current\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "path\to\python\current\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "path\to\python\current\Scripts\enex2notion.exe\__main__.py", line 7, in <module>
  File "path\to\python\current\lib\site-packages\enex2notion\cli.py", line 128, in main
    cli(sys.argv[1:])
  File "path\to\python\current\lib\site-packages\enex2notion\cli.py", line 123, in cli
    enex_uploader.upload(enex_input)
  File "path\to\python\current\lib\site-packages\enex2notion\cli.py", line 64, in upload
    note_blocks = self._parse_note(note)
  File "path\to\python\current\lib\site-packages\enex2notion\cli.py", line 74, in _parse_note
    return parse_note(
  File "path\to\python\current\lib\site-packages\enex2notion\note_parser.py", line 27, in parse_note
    note_blocks = parse_note_blocks(note_dom)
  File "path\to\python\current\lib\site-packages\enex2notion\note_parser_blocks.py", line 38, in parse_note_blocks
    block = _parse_block(child)
  File "path\to\python\current\lib\site-packages\enex2notion\note_parser_blocks.py", line 77, in _parse_block
    return tag_parser(element)
  File "path\to\python\current\lib\site-packages\enex2notion\note_parser_e_table.py", line 9, in parse_table
    rows = _convert_table_into_rows(element)
  File "path\to\python\current\lib\site-packages\enex2notion\note_parser_e_table.py", line 26, in _convert_table_into_rows
    longest_row = max(len(r) for r in rows)
ValueError: max() arg is an empty sequence
  • --done-file オプションでハッシュ値のファイル出力先を指定し、インポートが中断されたところからリジューム実行できるようなことが書いてあるが、試した限りではいまいち判定がうまく動いてくれず、普通に二重に取り込まれてしまった
    • なので中断されると再実行が大変なので、安定した回線で、十分な時間のあるときに実行するのがオススメ

備考

このツールはたぶんNotionの非公式APIと呼ばれるものを使っていると思いますが、現在Notionでは公式APIも公開されています。

なのでこの非公式APIがいつまで利用できるかは不明。

公式APIを使って自前でインポートスクリプト書くという手もありかもです。