このメモについて

もういまは Evernote から別の環境に乗り換えてしまったけど・・・
2022年1月頃、Evernote から他に乗り換えるためにノートのデータをテキスト化することを検討していた。
そのときのメモ。
もう使えるかわからないし、情報の正しさもいまさら検証していないけど、ここに置いておく。

データファイル仕様調査

Evernote からエクスポートした HTML は「単一のWebページ」も「複数のWebページ」もほぼ同じ HTML 構造。

ノート1つ分のデータは <div class="html-note"> に入っている。
タイトルは <meta itemprop="title" content="2022/01/30"> に。
ノート本文は en-note 要素の中。

ノートの本文は en-note 要素に入ってる。


<div class="html-note">
  <meta itemprop="title" content="2022/01/30">
  <meta itemprop="created" content="20220129T231052Z">
  <meta itemprop="updated" content="20220129T231654Z">
  <meta itemprop="tag" content="WriteNote">
  <note-attributes>
  </note-attributes>
      
  <h1
    style="font-family: Source Sans Pro,-apple-system,system-ui,Segoe UI,Roboto,
      Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
      overflow-wrap: break-word;
      page-break-inside: avoid;
  ">
    2022/01/30
  </h1>
  <en-note class="peso">
    <div class="para">Hello, World.</div>
    <div class="para">こんにちは、世界。</div>
    <div class="para">🐱🍣</div>
    <div class="para">[01:01]</div>
    <div class="para"><br></div>
    <div class="para">CSV (カンマセパレーティッドバリュー)</div>
    <div class="para">123,abc,あいう</div>
    <div class="para">"123","abc","あいう"</div>
    <div class="para"><br></div>
    <div class="para">HTML風に書いたテキスト</div>
    <div class="para">&lt;div&gt;</div>
    <div class="para">&nbsp;&nbsp;&lt;div&gt;</div>
    <div class="para">&nbsp;&nbsp;&nbsp;&nbsp;&lt;span&gt;HTML風&lt;/span&gt;</div>
    <div class="para">&nbsp;&nbsp;&lt;/div&gt;</div>
    <div class="para">&lt;/div&gt;</div>
    <div class="para"><br></div>
    <div class="para">Markdown風に書いたテキスト</div>
    <div class="para">```</div>
    <div class="para">4.times {</div>
    <div class="para">&nbsp;&nbsp;puts 'Markdown風'</div>
    <div class="para">}</div>
    <div class="para">```</div>
    <div class="para"><br></div>
    <div class="para">タブ</div>
    <div class="para">TAB-BEFORE	TAB-AFTER</div>
    <div class="para">TAB-BEFORE	TAB-AFTER</div>
    <div class="para"><br></div>
    <div class="para">空白2文字</div>
    <div class="para">TWO-SPACES-B  TWO-SPACES-A</div>
    <div class="para">TWO-SPACES-B  TWO-SPACES-A</div>
    <div class="para">[08:10:51]</div>
    <div class="para"><br></div>
  </en-note>
</div>

Evernote から enex 形式でエクスポートしたものは
ノート1つ分のデータは <note> 要素に入っている。
タイトルは <title>2022/01/30</title> 要素に。
ノート本文は <content> 要素の中に <![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?> で埋め込み。


<title>2022/01/30</title>
<created>20220129T231052Z</created>
<updated>20220129T231654Z</updated>
<tag>WriteNote</tag>
<note-attributes>
</note-attributes>
<content>
  <![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  <!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
  <en-note>
    <div>Hello, World.</div>
    <div>こんにちは、世界。</div>
    <div>🐱🍣</div>
    <div>[01:01]</div>
    <div><br /></div>
    <div>CSV (カンマセパレーティッドバリュー)</div>
    <div>123,abc,あいう</div>
    <div>"123","abc","あいう"</div>
    <div><br /></div>
    <div>HTML風に書いたテキスト</div>
    <div>&lt;div&gt;</div>
    <div>&nbsp;&nbsp;&lt;div&gt;</div>
    <div>&nbsp;&nbsp;&nbsp;&nbsp;&lt;span&gt;HTML風&lt;/span&gt;</div>
    <div>&nbsp;&nbsp;&lt;/div&gt;</div>
    <div>&lt;/div&gt;</div>
    <div><br /></div>
    <div>Markdown風に書いたテキスト</div>
    <div>```</div>
    <div>4.times {</div>
    <div>&nbsp;&nbsp;puts 'Markdown風'</div>
    <div>}</div>
    <div>```</div>
    <div><br /></div>
    <div>タブ</div>
    <div>TAB-BEFORE	TAB-AFTER</div>
    <div>TAB-BEFORE	TAB-AFTER</div>
    <div><br /></div>
    <div>空白2文字</div>
    <div>TWO-SPACES-B  TWO-SPACES-A</div>
    <div>TWO-SPACES-B  TWO-SPACES-A</div>
    <div></div>
    <div>[08:10:51]</div>
    <div><br /></div></en-note>      ]]>
    </content>
  </note>

データファイルからテキストを抽出する Python スクリプト


import sys
import os
import re
from html.parser import HTMLParser
from bs4 import BeautifulSoup
from bs4.element import ResultSet, Tag


# Evernote からエクスポートした HTML ファイル (複数のノート要素を含む HTML に対応) を
# ノート要素 (<div class="html-note">) のリスト (ResultSet) で返す
def get_html_note_list(input_html_file: str) -> ResultSet:
  with open(input_html_file, mode='rt', encoding='utf-8') as file:
    soup = BeautifulSoup(file, 'html.parser')
    return soup.find_all('div', {'class': 'html-note'})


# en-note 要素をテキスト化するクラス
# e.g. <en-note class="peso"><div class="para">Hello</div><div class="para"><br></div></en-note>
class EnNoteElementToText(HTMLParser): # HTMLParser を継承

  text: str = '' # テキスト化した文字列を溜め込む

  def handle_endtag(self, tag): # div 要素の終了タグが来たら改行を入れる
    if tag == 'div':
      self.text += '\n'

  def handle_data(self, data): # 要素内のテキストを溜め込む
    self.text += data.replace('\xa0', ' ') # &nbsp; が来たら半角スペースに置き換える


# ノート1つを表すクラス
class Note:

  def __init__(self, html_note: Tag):

    # タイトルを抜き出す
    # e.g. <div class="html-note"><meta itemprop="title" content="2022/01/30">
    itemprop_title = html_note.find('meta', {'itemprop': 'title'})
    self.title: str = itemprop_title['content'] # タイトル

    # 本文をテキスト化する
    en_note = html_note.find('en-note')
    en_note_html = str(en_note)
    parser = EnNoteElementToText()
    parser.feed(en_note_html) # en-note 要素をテキスト化
    parser.close()
    self.content: str = parser.text # テキスト化した本文


# ノート情報を標準出力へ出力
def print_note(note: Note) -> None:
  print('-------------------------------------------------------------------')
  print('Title: ' + note.title)
  print('-------------------------------------------------------------------')
  print(note.content, end='')
  print('-------------------------------------------------------------------')
  print()


# 出力ファイル名を決める
def get_text_file_name(note: Note) -> str:
  return re.sub(r'[¥\\/:*?"<>|@.]', '', note.title) + '.txt' # ファイル名に使いたくない文字を削る


# ノート情報をテキストファイルへ出力
def note_to_textfile(note: Note, output_file: str) -> None:
  with open(output_file, mode='xt', encoding='utf-8') as file: # すでに存在している場合は FileExistsError
    file.write(note.title + '\n')
    file.write('\n')
    file.write(note.content)
    file.write('\n')


# メイン処理
input_html_file = sys.argv[1] # Evernote からエクスポートした HTML ファイルのパス
html_note_list = get_html_note_list(input_html_file) # HTML ファイルを <div class="html-note"> 毎に分割してリスト化
print('ノートの件数: ' + str(len(html_note_list)))
print()
for html_note in html_note_list: # ノートを1つずつ処理
  note = Note(html_note) # <div class="html-note"> 要素からタイトルと本文をテキスト化
  print_note(note) # ノート情報を標準出力へ出力
  output_text_file = get_text_file_name(note) # 出力ファイル名を決める
  note_to_textfile(note, output_text_file) # ノート情報をテキストファイルへ出力
  print('出力ファイル名: ' + output_text_file)

tags: evernote python

Posted by NI-Lab. (@nilab)