Hatena::ブログ(Diary)

XXXannex このページをアンテナに追加 RSSフィード

2018-05-29

EvernoteのAPIを使ってみる

Documentation - Evernote Developers

Perlでいいかなぁと思ったけど公式ドキュメントが不親切なのでPythonにする。

Evernote Cloud API ? Python Quick-start Guide - Evernote Developers

getting startedドキュメントによると、まずは

  1. sendboxのアカウントを取得する
  2. Evernote API key申請をする

次にSDKインストールする。

$ pip install evernote

を実行するとエラーになる・・・

GitHub - evernote/evernote-sdk-python: Evernote SDK for Python

GitHub - evernote/evernote-sdk-python3: Testing the Evernote Cloud API for Python 3

This is a test SDK! The official Evernote SDK for Python doesn't support Python 3 yet; this repository is an experiment as we try to migrate.

えぇ・・・

今の時代はPython3だと聞いたのでPython2とか入れてないんだけど。

404 Not Found

なるほどね〜

というわけでCygwinPythonアンインストールしてWindows版にする。

$ py -2 -m pip install evernote

うまくいった。

一通り環境はできたのでサンプルコードを書きたい。が、その前にアクセストークンを取得しておく。

Developer Tokens - Evernote Developers

sandboxのdeveloper tokenはすぐに取得できるけど、productionのdeveloper tokenは "Update: the creation of developer tokens is temporarily disabled." などというメッセージが出て取得できないようになってる。

仕方がないのでsupportのページからAPI keyのactivation申請をする

Support - Evernote Developers

に行くと"Activate an API Key"というボタンがあるので適当入力する。

How do I copy my API key from Sandbox to www (production)?


This process is called key activation. To activate your API key on Evernote’s production servers, file a key activation request using this form. Once we’ve determined that your integration fulfills all of the requirements of a production application, your key will be activated within 1?2 business days. Youll be notified when your key has been activated (or if there’s an issue with your application that needs to be addressed).

申請項目のpermissionというのがよく分からんかったけど、多分ここのページに乗ってる意味でのpermissionなんだと思う。

API Key Permissions - Evernote Developers

とりあえず "Full access" permissionを下さいな、と書いておいた。

サンプルコード

ノートブックとノートブック内のノートリストを表示する。

from evernote.api.client import EvernoteClient
from evernote.edam.notestore.ttypes import NoteFilter, NotesMetadataResultSpec

dev_token = "<your developer token>"
client = EvernoteClient(token=dev_token)

userStore = client.get_user_store()
user = userStore.getUser()
print "username : " + user.username + "\n"

offset = 0
max_notes = 10

noteStore = client.get_note_store()
notebooks = noteStore.listNotebooks()

for n in notebooks:
	print "* " + n.name
	filter = NoteFilter(notebookGuid=n.guid)
	result_spec = NotesMetadataResultSpec(includeTitle=True)
	result_list = noteStore.findNotesMetadata(dev_token, filter, offset, max_notes, result_spec)
	for note in result_list.notes:
		print "\t- %s" % note.title
$ py -2 a.py
username : kobayashi01234

* <Inbox>
* プロジェクトその1
* testbook1
        - testnote001

なるほどね。

シンプルノートブック名を指定してノート一覧を取得できればいいのだけど、そういう機能はないっぽい。

Get a notebook by name and its notes - Evernote API Discussion - Evernote User Forum

追記

API keyのactivation mailが6/3朝(USだとすると現地時間では6/2夕方)くらいに来ていた。結構すぐに来るものなんだな。そして適当申請してもだいじょうぶそうw

2018-05-28

CygwinでHTTPS接続できないときはca-certificatesをチェックしよう

何度かブログで触れている気がするdropbox-apiスクリプト

https://github.com/s-aska/dropbox-api-command

いつの頃からか(今思えばCygwinパッケージをアップグレードした辺りから・・・エラーになった。

$ dropbox-api ls /sync
2018-05-28T16:51:48 [WebService::Dropbox] [ERROR] https://api.dropboxapi.com/2/files/list_folder {"path":"/sync"} -> [500] Can't connect to api.dropboxapi.com:443

なんやねん、dropboxが落ちてるのかAPIが終了になったのか・・・と思ってcurlでチェックしてみるとAPI問題なく動作してるっぽい。

$ curl -k -X POST https://api.dropboxapi.com/2/files/list_folder \
>     --header "Authorization: Bearer $ACCESS_TOKEN" \
>     --header "Content-Type: application/json" \
>     --data "{\"path\": \"/sync\"}"

ちなみに-kをつけないとcertificateのエラーが出る(これも伏線)。

curl: (77) error setting certificate verify locations:
  CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none

小一時間悩んで、そもそもPerlHTTPSアクセスができないのでは・・・と思って簡単スクリプトでチェックしてみたらビンゴっぽい。

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Request;

my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new( GET => 'https://www.flickr.com/');
my $response = $ua->request($req);

### $response
### $response: bless( {
###                     _content => 'Can\'t connect to www.flickr.com:443',
..
###                     _msg => 'Can\'t connect to www.flickr.com:443',
###                     _rc => 500,

PerlというかLWPでHTTPSコンテンツが取得できない、ってわりと良くある割に分かりにくい問題で、一昔前ならNet::SSLeayをインストールしましょうみたいなのがテンプレだったのだけど。

PerlでSSL(https)のサイトのコンテンツが取得できない - ノウハウブログ - カンタローCGI

一通り必要そうなモジュールをチェックしてみたけど導入済みだし、何やろうなあ。

"perl https 500 error"で検索したら最初に出てくるサイト

perl - 500 error with LWP:UserAgent - Stack Overflow

"verify_hostname=0" ねえ・・・そういや上のcurlでも証明書エラーが出てたな、と思って試してみたら動くではないか

というわけで、当初の問題環境変数 PERL_LWP_SSL_VERIFY_HOSTNAME=0 を指定すれば解決

$ PERL_LWP_SSL_VERIFY_HOSTNAME=0 dropbox-api ls /sync

・・・解決はするけど、毎度毎度証明書無視するオプションを付けるのは面倒だしセキュリティ的にもいかがなものかと思うので、もう少し調べる。

と、Cygwinでは ca-certificates パッケージが証明書ファイルを持っているようなので再インストールしてみると環境変数指定しなくても動くようになった。

よかったよかった。それにしてもHTTPSというかSSLというか証明書周りは面倒くさいなあ・・・

2018-03-30

10年遅いPerlのourの話

10年ぶりにPerlを書いて色々調べていたら超懐かしい話題を見かけたので。

ourの挙動は分かったけど、一番重要なのはそれで何が嬉しいの?という事じゃないのかなあ。

要するにuse strict環境グローバル変数(完全修飾名?)を簡単に作れる方法というだけ。

use strict;

package VERY::LONG::NAME::MODULE{
  # $var = 1; これはエラー
  # $VERY::LONG::NAME::MODULE::var = 1; これはOKだけど面倒くさい
  # my $var = 1; スコープを外れると見えなくなる(undef)
  our $var = 1;
}
print "$VERY::LONG::NAME::MODULE::var\n";

Wunderlist APIを使ってみた

Wunderlistの完了済みタスクコピーしてEvernoteに保存してタスクを削除、というのを毎週するようにしたのだけど、どうにも面倒くさい。

Documentation | Wunderlist Developer

API提供されているから自動化できそう。

検索するとPerlではAPI::Wunderlistというモジュールが公開されているのだけど、インストールがどうも上手く行かなくて、強制インストールするとコードエラーになる。

no title

どうやらType::Tinyのテストが失敗するようなのだけど、コード抽象的すぎてよく分からんし諦めた。

そもそもX-Client-IDとX-Access-Tokenを付けたHTTPを投げるだけなんだからcurlを使ったシェルスクリプトでもいいんだよね。PerlならLWPをそのまま使ってもいいし。何でインストール四苦八苦してまでモジュールを入れんといかんねん。

こういうマイクロ実装ってライブラリの導入とか依存関係とかで困るから結局モノリシックものが便利なんじゃないかという気になってきた(老化)

余談はともかく。完了したタスクノートリストアップするスクリプト作ってみた

LWPでもいいけど、REST::ClientといのがLWPベースシンプル実装っぽいので使うことにした。

use strict;
use warnings;
use Carp qw/croak/;

use REST::Client;
use HTTP::Response;
use JSON::XS;
use YAML::Syck;

my $JSON   = JSON::XS->new->utf8;
my $API    = 'a.wunderlist.com/api/v1';
my $client = REST::Client->new();
my $res;

$client->addHeader('X-Client-ID', $CLIENT_ID);
$client->addHeader('X-Access-Token', $ACCESS_TOKEN);

# 1. get lists
$client->GET("$API/lists");
$res = res_with_check($client);
### $res

# 2. get completed tasks and notes
my @ctasks;
foreach my $l (@$res){
  ### $l
  $client->GET(sprintf("$API/tasks?completed=true&list_id=%s", $l->{id}));
  $res = res_with_check($client);
  ### $res
  foreach my $t (@$res){
    ### $t
    $client->GET(sprintf("$API/notes?task_id=%s", $t->{id}));
    $res = res_with_check($client);
    ### $res
    push(@ctasks, {
      id           => $t->{id},
      list         => $l->{title},
      title        => $t->{title},
      created_at   => $t->{created_at},
      completed_at => $t->{completed_at},
      note         => ($$res[0]->{content} || ''),
    });
  }
}
### @ctasks

DumpFile("wl_completed.yml", \@ctasks);

sub res_with_check{
  my $client = shift;
  my $code = $client->responseCode();
  if($code ne '200'){
    my $err = sprintf("Server Error: %s\n", HTTP::Response->new($code)->status_line);
    $err .= $client->responseContent() . "\n" if $client->responseContent();
    croak $err;
  }
  my $data = $client->responseContent();
  my $tree = $JSON->decode($data);
  return $tree;
}

2017-11-25

Firefox webdriverでcookieが読めなくなった話

取得したクッキーを保存して読み直すだけのコードである

from selenium import webdriver
import json

driver = webdriver.Firefox()
#driver = webdriver.Chrome()
#driver = webdriver.PhantomJS()

try:
    driver.get("http://www.google.com")

    fw = open('cookies.json','w')
    json.dump(driver.get_cookies(),fw,indent=4)
    fw.close()
    
    f = open("cookies.json")
    cookies = json.load(f)
    f.close()

    for cookie in cookies:
        driver.add_cookie(cookie)

finally:
    driver.quit()

最近Firefoxバージョンを最新(57)に上げたら、上のコードエラーになった。

$ python test.py
Traceback (most recent call last):
  File "test.py", line 24, in <module>
    driver.add_cookie(cookie)
  File "/usr/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 760, in add_cookie
    self.execute(Command.ADD_COOKIE, {'cookie': cookie_dict})
  File "/usr/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 308, in execute
    self.error_handler.check_response(response)
  File "/usr/lib/python3.6/site-packages/selenium/webdriver/remote/errorhandler.py", line 194, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: .google.co.jp

よく分からん

ドメインの先頭にドットがついてるとエラーになる(InvalidCookieDomainException)という話もあるみたいだけど、generalなWebDriverExceptionとしか出てこないので、やっぱりよく分からん

先頭のドットを消すと確かにExceptionを出さずに動くようになるのだが、そうすると目的プログラムcookieを期待通りに読んでくれなくなって詰む。

仕方ないのでChromeを使うか・・・と思うと、今度はswitch_to.windowでactiveにしたウインドウ(タブ)は必ずフォアグラウンドになってしまうというアレな仕様で詰む。(参考:ウインドウの最小化 - XXXannex

Seleniumを使うと、だいたいあちらを立てればこちらが立たぬ・・・みたいなことが多くてウンザリする。

iMacrosを自動起動する方向に変換した方がいいのかもしれない。まあ、あちらはあちらでwindows nativeなことができなくて困りそうだけど。

ま、いいや。とりあえず適当に53(53.0.3)くらいまで戻して事なきを得た。二度とアップグレードしないぞ。

追記

自動更新をOFFにし忘れたせいでまた57にアップグレードされてしまった。切れそう。

2017-11-10

○○ソートみたいなやつ

ソートファクトリー

こういうの。

use strict;
use warnings;
use Path::Class;

my @list  = file('list.txt')->slurp(chomp=>1);
my $COUNT = 0;
print "$_\n" foreach sort { compare($a, $b) } @list;

sub compare{
  my($lhs, $rhs) = @_;
  printf("#%02d: which do you like?\n", ++$COUNT);
  print "1. $lhs\n";
  print "2. $rhs\n";
  print "3. Even\n";
  my $input = <>; $input =~ tr/\x0A\x0D//d;
  if($input == 1){
    return -1;
  }
  elsif($input == 2){
    return 1;
  }
  elsif($input == 3){
    return 0;
  }
  else{
    die;
  }
}