それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Evernoteをプログラムから使う。Ruby編

Evernote便利です。画像とテキストをまとめて保存できて、リンクを貼れるのでとても便利です。以前はMhtmlを使っていましたが最近はすっかりEvernoteです。いくら便利でも限界はあります。どうしても自動化や手の届かない処理はプログラムで書きたくなります。そこでプログラムからEvernoteにアクセス方法を調べて一括管理することにしました。

EvernoteAPIを使ってRubyでノートを扱う方法

Evernoteをプログラムから扱うには、次のようにします。

  1. Sandbox(砂場)をつかってプログラムを実験する
    • アカウントを作る
    • アレコレ実験する
  2. 正式版のEvernoteのノートを扱う。

Sandboxで実験はしておいたほうがいいでしょう。うっかり消しちゃうとか制限いっぱいまでアップしちゃうといったトラブルを回避できる。

EvernoteのAPIアクセスに必要なもの

  • 通常アカウント
  • APIキー
  • oauthのアクセストークン

です。

EvernoteAPIについて知っておくべきこと

  • アカウントへのアクセスはアクセストークンを使って行う。
  • アクセストークンはアカウントへのフルコントロールをもらった合鍵みたいなもの*1
  • APIはThriftの手順に従う。*2
  • 僕はThriftを使わず、Gemを使う。*3

Sandboxの使い方。

evernote sandbox test serverにアクセスして、アカウントを作ります。

次に、APIキーを発行してもらいます。

ウェブサービスAPI にアクセスしてAPIキーを発行します。

Rubyの準備

とりあえずEvernoteパッケージでいいでしょう。Thriftから叩いてもいいけど。まずはコレでいいですよ。

gem install evernote

アクセストークンの発行

gemの準備ができたら、次はアクセストークンを作ります。アカウントとAPIキーを関連付けます。

access_toke.rb
#!/usr/bin/env ruby
#evernote のアクセストークンを取得する
require "rubygems"
require 'evernote'

user_store_url = "https://sandbox.evernote.com/edam/user"

config = {
            :username => "takuya****_sandbox",
            :password => "******",
            :consumer_key    => "takuya_1st-*****",
            :consumer_secret => "******",
         }

user_store = Evernote::UserStore.new(user_store_url, config)

auth_result = user_store.authenticate
user = auth_result.user
auth_token = auth_result.authenticationToken
puts "Authentication was successful for #{user.username}"
puts "Authentication token = #{auth_token}"
実行

実行するとTOKEが出てきます。

takuya@air:~/Desktop$ ruby access_token.rb 
Authentication was successful for takuya_1st_sandbox
Authentication token = S=s1:U=***:E=***:C=1**:P=37:A=takuya_****:H=********

このアクセストークンを使ってEvernoteのアカウントにアクセスできるようになります。アクセス関連のエラーがでたら、このプログラムでTOKENを再発行し、またやり直せばいいと思います。

Evernoteにノートを追加する。

デフォルトのノートブックにノートを追加する。

作ったアクセストークンを使って、アカウントにアクセスできるようなったので、ノートを追加してみます。

require "rubygems"
require 'evernote'
#先程取得したToken
auth_token = "S=s1:U=****:E=*******:C=*******:P=**:A=takuya_****:H=***"

#ノートのエンドポイントのURL
note_store_url = "http://sandbox.evernote.com/edam/note/s1"
# 正しくは以下のような形式じゃないとダメらしいけど、決め打ちでOKだった。
# note_store_url = "http://sandbox.evernote.com/edam/note/#{user.shardId}" 

note_store = Evernote::NoteStore.new(note_store_url)
notebooks = note_store.listNotebooks(auth_token)
default_notebook = notebooks[0]

#ノートを作る
note = Evernote::EDAM::Type::Note.new()
note.title = "hello Evernote world"                                          ## タイトル
content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +      ## 内容 ENML形式
  "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml.dtd\">" +
  "<en-note>" + 
  "my first note from ruby " +
  "</en-note>"
note.content = content

begin
createdNote = note_store.createNote(auth_token, note) #ノートを保存
rescue => e
  puts e
  puts "ENMLシンタックスエラー" if  e.errorCode == Evernote::EDAM::Error::EDAMErrorCode::ENML_VALIDATION
end

ノートの確認

サンドボックス テストページにログインして、確認してみるのが一番早い

次に写真のアップロード

写真は添付ファイルとしてアップロードします。

Evernoteは画像が貼り込めるから便利なんですよね。画像を扱えないと魅力が半減です。まず画像の扱い方を覚えましょうか

upload.rb

画像を添付するスクリプト

require "rubygems"
require 'evernote'
require "mime/types"
require 'base64'
auth_token = "S=s1:U=****:E=*******:C=*******:P=**:A=takuya_****:H=***"

#添付したい写真を読み込んで、MD5でハッシュキーを作る。
filename = "vlcsnap-00007.png"
mime_type = MIME::Types.type_for(filename).to_s
image = File.open(filename, "rb") { |io| io.read }
hashFunc = Digest::MD5.new
hashHex = hashFunc.hexdigest(image)

#添付ファイルのメタデータ形式を作る。
data = Evernote::EDAM::Type::Data.new()
data.size = image.size
data.bodyHash = hashHex
data.body = image
 
#dataと併せて添付ファイル形式を作る
resource = Evernote::EDAM::Type::Resource.new()
resource.mime = "image/png"
resource.data = data
resource.attributes = Evernote::EDAM::Type::ResourceAttributes.new()
resource.attributes.fileName = filename

#ノートブックを取得して
note_store_url = "http://sandbox.evernote.com/edam/note/s1"
note_store = Evernote::NoteStore.new(note_store_url)
notebooks = note_store.listNotebooks(auth_token)
default_notebook = notebooks[0]

#ノートを新規作成。
note = Evernote::EDAM::Type::Note.new()
note.title = "hello Evernote upload"
content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + 
  "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml.dtd\">" +
  "<en-note>" + 
  "image from ruby " +
   '<en-media type="' + mime_type +'" hash="' + hashHex + '"/>' +
  "</en-note>"

note.content = content
note.resources = [ resource ] #リソースを貼付け
#ノートをサーバーへ
createdNote = note_store.createNote(auth_token,  note)

puts :end

Sandboxで確認

写真のアップロードを確認してみます。

サンドボックスで確認しました。アップロードできています。


ノートの中身を取得するには

ここまでできたら、あとはAPIの叩き方をパターンで覚えるだけです。どんどん行きましょう

#ノートの中身を取る
id = note_list.notes.last.guid #ノートのIDを取得して
content = note_store.getNoteContent auth_token, id #取得

ノートには guid という一意な識別子が付与されていて、それを指定しノートの中身を取り出します。

ノートの一覧を取得する。

ではノートの一覧も取得してみましょう


ノートの一覧を表示するには、ノートブックの中に検索条件を指定して、取り出します。

##検索条件を作る。
filter = Evernote::EDAM::NoteStore::NoteFilter.new
#デフォルトノートブック中のノート一覧を探す。
    #ノートブックのIDを取得し検索条件に入れる。
filter.notebookGuid = default_notebook.guid
#取得する最大数を設定
limit = 1000
#何件目から取得するか
offset  =0
note_list = note_store.findNotes auth_token, filter, offset, limit 
note_list #http://www.evernote.com/about/developer/api/ref/NoteStore.html#Struct_NoteList
#表示してみる
note_list.notes.each{|e| puts [e.title,e.created,e.content].inspect} #ノートのタイトルやメタデータは取れるよ。でも中身はないよ!

limit や offset があるのは、EvernoteがフロントエンドのデータストレージにSQLiteを使っていることから、SQLへの呼び出しだと推測できる。

ノートに付けられたタグ一覧を取得する

ノートにつけたタグも、ノートのguidを指定して、そこから関連付けて取り出します。

#ノートにつけた[タグ一覧]を取り出す。
id = note_list.notes.last.guid
labeled_tags = note_store.getNoteTagNames auth_token, id

つまりguidがすべてのキーになっているのですね。

ノートの属性情報にアクセスする。


ノートには「作成者」「緯度」「経度」「URL」などの拡張属性情報を持つことが出来ます。それらにアクセスしてみます。

#ノートにつけた属性情報一覧を取り出す。
# http://www.evernote.com/about/developer/api/ref/Types.html#Struct_NoteAttributes
attrs = note_list.notes.last.attributes
attrs.latitude #緯度
attrs.longitude #経度
attrs.author
attrs.sourceApplication 
attrs.sourceURL 
attrs.source

#ノートにつけた属性情報を更新する。
current = note_list.notes.last
attrs =current.attributes
attrs.author = "takuya"
attrs.placename = "takuya"
current.attributes = attrs
note_store.updateNote(auth_token,current)

ThriftのAPIや使えるメソッドは何処で調べるか?

Evernote API: All declarationsに細かく載っています。


大体は上記のサンプルコードを見ればわかると思いますが、名前空間は次のような感じになっていました。

Thriftの定義 evernoteパッケージの名前空間
Types Note Evernote::EDAM::Type::Note
Types Resource Evernote::EDAM::Type::Resource
Module NoteStore Evernote::NoteStore
Module NoteStore Types NoteFilter Evernote::EDAM::NoteStore::NoteFilter
Module UserStore Evernote::UserStore

ENMLとは?


DTDで定義されたEvernote独自のHTMLサブセット。

基本構造は次のような感じになっていました

<?xml version="1.0" encoding="UTF-8"?> 
  <!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml.dtd\">" +
  <en-note>
   this is a text . hello from ruby
   images from ruby
  <en-media type="image/png" hash="bdeasdf1234fa19"/>
  </en-note>

テキストと装飾以外の添付リンクファイルは、en-mediaタグに納めて、キーを元に resourcesに登録します。

ENMLで使えるタグ

ENMLのマニュアルによると、以下のタグが使えます。

A, ABBR, ACRONYM, ADDRESS, AREA, B, BDO, BIG, BLOCKQUOTE, BR, CAPTION, CENTER, CITE, CODE, COL, COLGROUP, DD, DEL, DFN, DIV, DL, DT, EM, FONT, H1, H2, H3, H4, H5, H6, HR, I, IMG, INS, KBD, LI, MAP, OL, P PRE, Q, S, SAMP, SMALL, SPAN, STRIKE, STRONG, SUB, SUP, TABLE, TBODY, TD, TFOOT, TH, THEAD, TITLE, TR, TT, U, UL, VAR, XMP

Cloud API - Evernote Developers


使えるリンクは次のリンクが使える

     http, https, file

使えるメタ属性

The EN-NOTE element supports the following optional attributes:
· bgcolor – background color of the note
· text – text color
· style, title, lang, xml:lang, dir – optional attributes with the same semantics as the corresponding XHTML standard attributes.

styleやタイトルは タグの属性値に入れるそうです。XML基準に従うとのこと

onclickなどは?on***などは保存できません。ENMLをアップロードするときにシンタックスエラーで落ちます。

img src=data は出来るの?

試したらできた。でも検索条件として使えなくなるので諸刃の刃。

使い方は以上になります。

ここまでて、Evernoteの処理の8割程度は出来るような基本操作が覚えられると思います。

あとはAPIを見ながら、面倒な事をどんどん自動化しちゃえばオッケー

Evernoteをプログラムから使うと・・・活用アイディア

たくさんの写真をまとめてアップロードしてまとめてダウンロードしたり、ファイルサイズがオーバーしそうなPDFはJPEGに分割して、複数ノートに自動分割ができそうですね。


また、WordpressのようなCMSのバックエンドにも使えそうです。


履歴のアクセスを使えば、バージョンを見ることができます。
タスク関連の属性を使えば、期日を設定したAPIアクセスができます。


なんというファイルシステムの拡張。

sandboxで実験したら

本番環境のアカウントにアクセスできるように、APIキーに許可を発行してもらいます。

Evernote便利。

2012-07-04追記

本番環境にアクセスするとエラー

Thrift::ProtocolException: No version identifier, old protocol client?

It happened to me. Make sure the sandbox URL is OK:

"https://sandbox.evernote.com/edam/user"

Do not replace the 'user' string in the URL path with your username.

'read_message_begin': No version identifier, old protocol client? (Thrift::ProtocolException) · Issue #9 · cgs/evernote · GitHub

ということらしい。これは endpoint のURLが間違っているときに起こるそうだ。ちなみに私はwwwwと、wwwが一つ多かった。猛反省

*1:OAuthですね

*2:一種のRPC。ThriftはRPCの完成形だと思う

*3:僕達ゆとり世代(自称)は面倒くさいことは省いてgemでいい、だけどThriftが裏で動いて、メソッド呼び出し時に都度都度APIアクセスしてることは意識しておくといい