はてなブログAtomPub - Hatena Developer Center を使ってはてなブログの情報を取得したり投稿したりしてみる。
OAuth認証
はてなブログAtomPub を利用するために、クライアントは OAuth 認証、WSSE認証、Basic認証のいずれかを行う必要があります。
http://developer.hatena.ne.jp/ja/documents/blog/apis/atom?kid=238#auth
とのこと。ここではOAuth認証を使ってみることにする。
はてな OAuth - Hatena Developer Center を読みつつ、まずはアプリケーションを登録して、Consumer KeyとConsumer Secretを取得。
Consumer key を取得して OAuth 開発をはじめよう - Hatena Developer Center に、PerlでMojolicious::Lite
を使ってWebアプリでAccess Tokenを取得する例が載っているけど、ここでは"oob"を使ってWebアプリを立ち上げずにターミナル上で完結する方式で取得してみる。
#!/usr/bin/env perl use strict; use warnings; use Config::Pit; use OAuth::Lite::Consumer; my $config = pit_get('hatena.ne.jp', require => { consumer_key => '<oauth consumer key>', consumer_secret => '<oauth consumer secret>', }); my $consumer = OAuth::Lite::Consumer->new( consumer_key => $config->{consumer_key}, consumer_secret => $config->{consumer_secret}, site => 'https://www.hatena.com', request_token_path => '/oauth/initiate', access_token_path => '/oauth/token', authorize_path => 'https://www.hatena.com/oauth/authorize', ); my $request_token = $consumer->get_request_token( callback_url => 'oob', scope => 'read_private,write_private', ); my $url = $consumer->url_to_authorize(token => $request_token); print "open ${url} and authorize.", "\n"; print "enter verification code: "; my $verifier = <STDIN>; chomp $verifier; my $access_token = $consumer->get_access_token( token => $request_token, verifier => $verifier, ); print "access_token: ", $access_token->token, "\n"; print "access_token_secret: ", $access_token->secret, "\n";
こんなかんじでcallback_url
にoob
を指定して認証用のURLを発行して開くと、認証した先でコードが表示されるので、この値をコピペして入力することでAccess Tokenが得られる。
はてなブログAtomPubでは"read_private"と"write_private"の権限が必要らしいのでscopeにはその2つを指定。
エントリの取得
上記でAccess TokenとAccess Token Secretを取得しておけば、それらを使ってはてなブログのエントリ取得/投稿などが出来るようになるらしい。
#!/usr/bin/env perl use strict; use warnings; use utf8; use Config::Pit; use OAuth::Lite::Consumer; use XML::Atom::Feed; my $hatena_id = shift or die; my $blog_id = shift or die; my $config = pit_get('hatena.ne.jp', require => { consumer_key => '<oauth consumer key>', consumer_secret => '<oauth consumer secret>', access_token => '<your access token>', access_token_secret => '<your access token secret>', }); my $consumer = OAuth::Lite::Consumer->new( consumer_key => $config->{consumer_key}, consumer_secret => $config->{consumer_secret}, ); $consumer->access_token( OAuth::Lite::Token->new( token => $config->{access_token}, secret => $config->{access_token_secret}, ), ); my $res = $consumer->get("https://blog.hatena.ne.jp/${hatena_id}/${blog_id}/atom/entry"); $res->is_success or die $res->code; my $xml = $res->decoded_content; my $feed = XML::Atom::Feed->new(\$xml); for my $entry ($feed->entries) { printf "%s - %s\n", $entry->published, $entry->title; }
普通に[]https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom[]
にGETリクエストを送るだけでXMLが返ってくるので、これらを適当にparseすればエントリの内容を取得したりできる。
次のページへのリンクが
<link rel="next" href="https://blog.hatena.ne.jp/sugyan/sugyan.hatenablog.com/atom/entry?page=1352341991" />
のような形で返ってくるけど、OAuth::Lite
の場合クエリパラメータを含むURLではなく"params"として別オプションで渡さないといけないようだ。
# これはダメ # my $res = $consumer->get('https://blog.hatena.ne.jp/sugyan/sugyan.hatenablog.com/atom/entry?page=1352341991'); # これならOK my $res = $consumer->get('https://blog.hatena.ne.jp/sugyan/sugyan.hatenablog.com/atom/entry', +{ params => +{ page => 1352341991 } });
エントリの投稿
上記と同じURLに対し、POSTで所定のXMLを送信することでエントリの投稿ができる。
#!/usr/bin/env perl use strict; use warnings; use utf8; use Config::Pit; use OAuth::Lite::Consumer; use XML::Atom::Feed; my $hatena_id = shift or die; my $blog_id = shift or die; my $config = pit_get('hatena.ne.jp', require => { consumer_key => '<oauth consumer key>', consumer_secret => '<oauth consumer secret>', access_token => '<your access token>', access_token_secret => '<your access token secret>', }); my $consumer = OAuth::Lite::Consumer->new( consumer_key => $config->{consumer_key}, consumer_secret => $config->{consumer_secret}, ); $consumer->access_token( OAuth::Lite::Token->new( token => $config->{access_token}, secret => $config->{access_token_secret}, ), ); my $entry = XML::Atom::Entry->new(Namespace => 'http://www.w3.org/2005/Atom'); $entry->title('投稿テスト'); $entry->add('', 'content', <<'CONTENT', +{ type => 'text/plain' }); てすと - hoge - fuga - piyo CONTENT my $res = $consumer->post("https://blog.hatena.ne.jp/${hatena_id}/${blog_id}/atom/entry", $entry->as_xml, +{ headers => [ 'Content-Type' => 'application/xml' ] }); $res->is_success or die $res->code; my $xml = $res->decoded_content; print $xml;
リクエストヘッダの"Content-Type"を指定していないとinvalid signatureになってしまっていてハマった…
下書きで投稿したい場合は"app:control/app:draft"要素を指定すれば良いらしい。
use XML::Atom::Util qw(create_element); ... my $entry = XML::Atom::Entry->new(Namespace => 'http://www.w3.org/2005/Atom'); $entry->set_attr('xmlns:app', 'http://www.w3.org/2007/app'); $entry->title('投稿テスト'); $entry->add('', 'content', '内容', +{ type => 'text/plain' }); my $draft = create_element('', 'app:draft'); $draft->appendText('yes'); my $control = create_element('', 'app:control'); $control->appendChild($draft); $entry->set('', '', $control);
こんなカンジでできた