zdogma's diary

徒然なるエンジニアな日々。

Kindle の Highlight を Evernote に同期するスクリプトを書いた

ことはじめ

Kindle の Highlight 機能、便利ですよね!
特にビジネス書や小説などで、後で振り返りたい内容・表現に対してラインマーカーの如く Highlight を残している方も多いと思います。
かくいう私自身も Highlight をたくさん残しているのですが、せっかく残したものの「これ、いつ見返せばよいのだろう...」という根本的な疑問がありました。

これまで読んだ本の中には積極的に読み返したい本もあればそうでない本もあるわけで、アプリでいちいち Highlight を確認するのは辛いし、 一応 Web にも自分の Highlight をまとめてくれている場所はあるのですが、毎回アクセスするには認証もあったりで大変です。

https://kindle.amazon.co.jp/your_highlights

f:id:z_dogma:20160626222158p:plain

ということで、「自分の Kindle の Highlight を Evernote にバッチで保存するようなスクリプトを書けばいいじゃん!」ということで、書きました。

github.com

実行すると、Evernote にこんな感じのノートブックが作られます。
(タイトルの先頭の★は、その本の中での Highlight 箇所の数です)

f:id:z_dogma:20160626221456p:plain

これで電車の中でも Highlight を読み返しやすいですね!

本題

使い方

1.README の Install を読み、必要なミドルウェアをインストールします。

需要がどれくらいあるかわからなかったので、 Web アプリケーションにはしていません。
Ruby で書いたスクリプトをそのまま叩く(バッチの場合は cron などはお任せ)という不親切仕様ですがご勘弁を...。

2.env ファイルを用意します。

env ファイルとは、環境変数を定義するための設定ファイルで、 dotenv という gem を使って読み込みを行っています。
今回動かすスクリプトで必要な設定は下記3つです。

# kindle で登録している amazon アカウントのメールアドレス
AMAZON_MAIL_ADDRESS=xxx@xxx.xxx
# kindle で登録している amazon アカウントのパスワード
AMAZON_PASSWORD=xxxxxxx
# Evernote のデベロッパートークン
EVERNOTE_DEVELOPER_TOKEN=xxxxxxxxxxxxxxx

上2つの amazon 系は特に説明は不要かと思いますが、いちばん下の Evernote の developer token に関しては、下記の URL にアクセスして取得してください。

https://www.evernote.com/api/DeveloperToken.action

これらを .env というファイルに書き込んで、下記の階層に置いてください。 下記のようになっていたら OK です!

$ tree -a
.
├── .bundle
│   └── config
├── .git
│   ├─ /* 省略 */
├── lib
│   ├── evernote.rb
│   └── kindle.rb
├── log
│   └── .gitkeep
├── .env
├── .gitignore
├── Gemfile
├── Gemfile.lock
├── README.md
└── kindle_evernote.rb
3.Evernote 側で受け口となるノートブックを作成します。

KindleHighlights というノートブックを Evernote 側で作成してください。
ちなみにノートブック名はスクリプトの中で決めうちで指定していますので、変えていただいても OK です。 (ノートブック作成部分もコードで書いとけよというツッコミは受け付けません←)

4.rubyスクリプトを叩きます。

README の Usage にある通りです。

bundle exec ruby kindle_evernote.rb

log/ 以下に進捗含めたログを吐くようにしているので、下記のような感じで別のウインドウで確認しながらやっていただくと捗るかもしれません。

less +F log/xxxxxxxx

解説

Kindle の情報を取得する部分

下記のサイト様の情報を参考にさせていただきました。
基本的に処理の流れは同じで、違いといえば追加で「Highlight の(本の中での)場所」も取得していることくらいです。
あと、タイトルなどでノイズが紛れ込んでいた場合に除く処理もちょこっと足しています。 qiita.com

ただ、やはりスクレイピングということで、HTML の構造が変わると取得できなくなるなどの脆弱さを孕んでいるので、しれっと動かなくなったらすみません。

def highlights_for(asin)
  sleep 0.3

  page = @mechanize_agent.get("https://kindle.amazon.co.jp/your_highlights_and_notes/#{asin}")

  highlights = page.xpath('//div[@class="highlightRow yourHighlight"]').map do |e|
    {
      text: e.xpath('span[@class="highlight"]')&.inner_text || '',
      location_href: e.css('a')&.attribute('href')&.value   || ''
    }
  end

  {
    title:  page.search('span.title').text.gsub(/\(Japanese Edition\)|\n.*\z/, ''),
    author: page.search('span.author').text.gsub(/\A by /, '').strip,
    highlights:  highlights
  }
end

Evernote でノートを作成する部分

こちらは本家のドキュメントに則って書きました。

ノートの作成 - Evernote Developers

機能要件として、スクリプトを打つたびに新しくノートが作られるのは嫌だったので、 すでにノートが存在すれば update、まだノートが無ければ create という形で処理を分けています。
ノートの存在確認のために一度 findNotes をするのですが、その時に一意に検索できるように ASIN をキーワードにしています。

コードとしてはこのあたりです。

kindle-highlights/evernote.rb at master · zdogma/kindle-highlights · GitHub

おわりに

ざーっと書いたため、いろいろ不親切な部分があると思いますが、もしよかったら使ってみてください!
ドキュメントの不備などあればコメントいただけると助かりますー!

ではでは!