僕らは偶然性を大切にするためにクソゲーを作り続ける。

なんか気ままにクソゲームを作っています。

BeautiflSoup + SQLAlchemyを使ってEvernoteの全ノートのテキストを抽出してSQLiteに保存する

こんにちは。むさしです。 しばらく更新してませんでしたが再開していきます^^;

最近はゲーム開発は止まっていて、仕事の関係でpythonを触っています。

evernoteの全文を抽出する必要がありその時に使った方法を公開します。 一番初めに思いつくことはEvernoteAPIを使う方法ですが色々面倒ですよね。 そこで、全ノートをhtml形式で出力してBeautifulSoupでスクレイピングする方法を紹介していきます。

Evernoteの全ノートをhtml形式で出力する

まず、Command + Aで全ノートを選択します。そこからノートをエクスポートします。

f:id:playwao:20150712112233p:plain

出力する形式はhtmlを選択してください。

f:id:playwao:20150712112240p:plain

今回はこれをmynoteとしてデスクトップに保存します。

mynoteのindex.htmlには出力したファイル全てのノートの目次になっていて、各htmlファイルへのリンクがあるのでそれを利用します。

手順としては

  • index.htmlから出力するノートのurlを抽出する。
  • url先からテキストを抽出する。
  • それをSQLiteに保存する。

というものです。

BeautifulSoupを使ってスクレイピングする

そもそもスクレイピングとはwebサイトから特定の情報を抽出したりする行為です。スクレイピングできるpythonのモジュールはいくつかありますが今回はBeautifulSoupというものを使っていきます。

まず、BeautifulSoupでインストールしましょう。

$ pip install beautifulsoup4

次にBeatifulSoupオブジェクトを作成しましょう。

import urllib2
from bs4 import BeautifulSoup

html = urllib2.urlopen("http://~ ~ ~")
soup = BeautifulSoup(html)

これでスクレイプする準備が出来ました。

あとは詳しくは公式ドキュメントをご覧ください。

http://kondou.com/BS4/

日本語で訳されているのでとっつきやすいと思います。

今回使うのはsoup.get_text()soup.find_all("a")note_url.get("href")のみです。

SQLAlchemyを使ってデータベースに保存する

SQLAlchemyとはORマッパーと言って、SQLで記述しなくてもデータベースとやりとりができる便利なやつです。 インストールはpipで行いましょう。

$ pip install sqlalchemy

全て説明すると長くなるのでこちらを確認ください。

http://www.sqlalchemy.org

また、こちらの日本語訳もありますがかなり古いのです。 ただ、英語よか読みやすいと思うのでこちらも参考にしてください。

http://omake.accense.com/static/doc-ja/sqlalchemy/ormtutorial.html

今回はメインはBeautiflSoupに関してなので後日分けて説明します。

Evernoteの文章を抽出する

これで準備は出来たのでスクレイピングしていきましょう。

まずはノートから文章のみを抽出して返す関数をつくります。

def scrape_evernote(url):

    tmp_url = "file:///(ノートのディレクトリ)" + url.encode('utf-8')
    html = urllib2.urlopen(tmp_url)
    soup = BeautifulSoup(html)
    all_items = soup.get_text()
    note = ""
    for word in all_items:
        note = note + word

    return note

最初の3行はBeautifulSoupオブジェクトを生成します。 all_items = soup.get_text()でurl先の全文を取得します。 そのあとの

note = ""
for word in all_items:
    note = note + word

という部分は、get_text()で取得できる文字は一文字づつ配列に入っているので配列全てを結合させる必要があります。

抽出したテキストをSQLiteに保存する

次に抽出したテキストをSQLiteに保存する関数を作ります。

Base = sqlalchemy.ext.declarative.declarative_base()

db_url = "sqlite+pysqlite:///evernote.sqlite3"
engine = sqlalchemy.create_engine(db_url, echo=True)

Base.metadata.create_all(engine)

# セッションを作成
Session = sqlalchemy.orm.sessionmaker(bind=engine)
session = Session()

#evernoteのモデルクラス
class Evernote(Base):
    __tablename__ = 'mynote'
    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
    title = sqlalchemy.Column(sqlalchemy.String)
    note = sqlalchemy.Column(sqlalchemy.String)

#...(略)...

def scrape():
    #indexから全ノートのurlを取得する
    index_url = "file:///(ノートのディレクトリ)/index.html"
    index_html = urllib2.urlopen(index_url)
    index_soup = BeautifulSoup(index_html)
    all_url = index_soup.find_all("a")

    for note_url in all_url:
        title = note_url.get_text()
        note = scrape_evernote(note_url.get("href"))
        evernote = Evernote(title=title, note=note)
        session.add(evernote)

    session.commit()

初めにBaseを作ります。(あんまりよくわかっていません^^;) そのあと、ノートのモデルを作成します。

class Evernote(Base):
    __tablename__ = 'mynote'
    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
    title = sqlalchemy.Column(sqlalchemy.String)
    note = sqlalchemy.Column(sqlalchemy.String)

今回はシンプルにノートのタイトルと内容だけ保存します。

db_url = "sqlite+pysqlite:///evernote.sqlite3"
engine = sqlalchemy.create_engine(db_url, echo=True)

Base.metadata.create_all(engine)

# セッションを作成
Session = sqlalchemy.orm.sessionmaker(bind=engine)
session = Session()

あとはSQLiteの保存場所をdb_url = "sqlite+pysqlite:///evernote.sqlite3"で指定してセッションを作成します。 Base.metadata.create_all(engine)evernote.sqlite3が存在しなかったら自動でevernote.sqlite3を作ってくれるます。 また、モデルクラスがあれば自動でテーブルの作ってくれて大変便利です。

最後にはindex.htmlから各ノートのタイトルとurlを取得します。 index.htmlの各ノートへのリンクは

<a href="ノートのurl">ノートのタイトル</a>

という構成になっているのでindex_soup.find_all("a")で全部のaタグを取得します。 各タグはそれぞれ配列で格納しているので、取り出してaタグからリンク先のurlとタイトルを取得します。 そのurlから先ほど作ったscrape_evernote()を使ってテキストを抽出します。 最後にコミットしてSQLiteに保存します。

これで抽出は完了です。

SQLiteで出力ではなくて例えばtxtデータに出力したい場合は

def scrape_txt():
    file = open('evernote_text.txt', 'w')

    #indexから全ノートのurlを取得する
    index_url = "file:///(ノートのディレクトリ)/index.html"
    index_html = urllib2.urlopen(index_url)
    index_soup = BeautifulSoup(index_html)
    all_url = index_soup.find_all("a")

    for note_url in all_url:
        title = note_url.get_text()
        file.write(title)
        note = scrape_evernote(note_url.get("href"))
        file.write(note)

    file.close()

とすれば可能です。 もちろんcsv形式の出力も出来ます。

まとめ

初めの方にも書きましたが大まかの手順は

  1. evernoteから文章を抽出したいノートをhtml形式で出力する。
  2. index.htmlから出力するノートのurlを抽出する。
  3. url先からテキストを抽出する。
  4. それをSQLiteに保存する。

となっています。 今回はテキストのみ抽出しましたが、ノート内の画像がノートのタイトルと同じ名前のフォルダに保存されています。 これをうまく使えばevernote内の画像を全て抽出することも可能だと思います。

完成データをgitにあげています。 https://github.com/musashi13z/evernote-scrape