OAuth::Liteを使って、はてなでOAuth認証 - koba::blogで認証はできたので、はてなのOAuth対応APIを使ってみる。
JSONを扱う必要があるので、以下のPerlモジュールをインストールした。
OAuth対応API
なぜかはてなのAPIはAtomインタフェースのものが多いのだが、JSONに対応している以下のAPIを使ってみた。
ユーザー情報を表示して、はてなハイクに投稿してみる。
これだけでは芸がないので、以下を参考にGoogle画像検索も使ってみた。
処理の流れ
今回はコードをちょっと整理して、path_info で処理を振分けるようにしました。
/ | ユーザ情報を取得して、ハイクのお題入力フォームを表示する。 |
/image | お題でGoogle画像検索し、画像選択とハイク本文入力フォームを表示する。 |
/post | ハイクを投稿し、ハイクのページにリダイレクトする。 |
/initiate | Request Token を取得する。(前回とほぼ同じなので説明は省略) |
/token | Access Token を取得する。(前回とほぼ同じなので説明は省略) |
/
request() を使ってAPIをコールする。
my $response = $consumer->request( method => 'GET', url => 'http://n.hatena.com/applications/my.json', token => $access_token, );
LWP::UserAgent とだいたい同じだけど、token に取得済みの Access Token を設定するとこが違う。
応答はJSON形式なので、from_json() を使ってデコードする。応答の本体を取得するときに decoded_content() を使用するのを忘れずに。
my $user = from_json($response->decoded_content);
以下の値が取得できます。
url_name | はてなid |
display_name | ニックネーム |
profile_image_url | プロフィール画像のURL |
/image
パラメータは以下。
- keyword
- ハイクのお題
request() を使ってGoogleのAPIをコールする。
my $response = $consumer->request( method => 'GET', url => 'http://ajax.googleapis.com/ajax/services/search/images', params => { v => '1.0', q => $cgi->param('keyword') }, );
Access Token は指定してはいけません(Access Token がGoogleにもれてしまうので)。本来は接続先ごとに LWP::UserAgent オブジェクトを用意するのが正しい作法だけど、今回は手抜き。
ごちゃごちゃとたくさん情報が返ってくるので、検索結果の配列(4つ返ってきます)だけを取得する。
my $results = from_json($response->decoded_content) ->{responseData}->{results};
配列内の以下の情報を使う。
unescapedUrl | 画像のURL(エスケープされていない) |
originalContextUrl | 画像を含むページのURL |
tbUrl | サムネイルのURL |
/post
パラメータは以下。
- keyword
- ハイクのお題
- image
- 画像のURLと画像を含むページのURLを改行で連結したもの
- body
- ハイク本文
my $response = $consumer->request( method => 'POST', url => 'http://h.hatena.ne.jp/api/statuses/update.json', params => { keyword => $keyword, status => "$image\n$body", source => $title }, token => $access_token, );
応答に投稿したハイクのURLがあるのでこれを取得してリダイレクトする。
my $url = from_json($response->decoded_content)->{link}; print $cgi->redirect($url);
全体のコード
#!/usr/bin/perl -T use strict; use CGI; use CGI::Carp qw(fatalsToBrowser); use OAuth::Lite::Consumer; use OAuth::Lite::Token; use JSON qw(from_json); my $consumer_key = '________________'; my $consumer_secret = '____________________________'; my $title = '絵俳句'; my $cgi = new CGI; $cgi->charset('utf-8'); my $consumer = new OAuth::Lite::Consumer( consumer_key => $consumer_key, consumer_secret => $consumer_secret, ); if ($cgi->path_info eq '/') { my $access_token = OAuth::Lite::Token->from_encoded( $cgi->cookie('HATENA_OAUTH')); my $response = $consumer->request( method => 'GET', url => 'http://n.hatena.com/applications/my.json', token => $access_token, ); if (! $response->is_success) { print $cgi->redirect($cgi->url.'/initiate'); exit; } my $user = from_json($response->decoded_content); print $cgi->header, $cgi->start_html( -title => $title, -encoding => 'utf-8', -lang => 'ja-JP', ), $cgi->h1($cgi->a({href=>$cgi->url},$title)), $cgi->img({src=>$user->{profile_image_url}}), $cgi->h2("$user->{display_name} (id:$user->{url_name})"), $cgi->start_form('GET', './image'), $cgi->textfield('keyword'), $cgi->submit, $cgi->end_form, $cgi->end_html; } elsif ($cgi->path_info eq '/image') { if (! $cgi->param('keyword')) { print $cgi->redirect($cgi->url.'/'); exit; } my $access_token = OAuth::Lite::Token->from_encoded( $cgi->cookie('HATENA_OAUTH')); my $response = $consumer->request( method => 'GET', url => 'http://n.hatena.com/applications/my.json', token => $access_token, ); if (! $response->is_success) { print $cgi->redirect($cgi->url.'/initiate'); exit; } my $user = from_json($response->decoded_content); my $image; my $response = $consumer->request( method => 'GET', url => 'http://ajax.googleapis.com/ajax/services/search/images', params => { v => '1.0', q => $cgi->param('keyword') }, ); if (! $response->is_success) { $image = $cgi->p($response->status_line); } else { my $results = from_json($response->decoded_content) ->{responseData}->{results}; foreach (@$results) { my $value = $_->{unescapedUrl}."\n".$_->{originalContextUrl}; $image .= $cgi->label( $cgi->input( {name=>'image',value=>$value,type=>'radio'}), $cgi->img({src=>$_->{tbUrl}}), $cgi->a({href=>$_->{originalContextUrl}},'(site)'), "\n", ); } } print $cgi->header, $cgi->start_html( -title => $title, -encoding => 'utf-8', -lang => 'ja-JP', ), $cgi->h1($cgi->a({href=>$cgi->url},$title)), $cgi->img({src=>$user->{profile_image_url}}), $cgi->h2("$user->{display_name} (id:$user->{url_name})"), $cgi->h3($cgi->param('keyword')), $cgi->start_form('POST', './post'), $cgi->hidden('keyword'), $image, $cgi->br, $cgi->textarea('body', '', 4, 40), $cgi->br, $cgi->submit, $cgi->end_form, $cgi->end_html; } elsif ($cgi->path_info eq '/post') { my $keyword = $cgi->param('keyword'); my $image = $cgi->param('image'); my $body = $cgi->param('body'); my $access_token = OAuth::Lite::Token->from_encoded( $cgi->cookie('HATENA_OAUTH')); my $response = $consumer->request( method => 'POST', url => 'http://h.hatena.ne.jp/api/statuses/update.json', params => { keyword => $keyword, status => "$image\n$body", source => $title }, token => $access_token, ); if (! $response->is_success) { print $cgi->header, $cgi->start_html( -title => $title, -encoding => 'utf-8', -lang => 'ja-JP', ), $cgi->h1($cgi->a({href=>$cgi->url},$title)), $cgi->h2($response->status_line), $cgi->p($response->decoded_content), $cgi->end_html; exit; } my $url = from_json($response->decoded_content)->{link}; print $cgi->redirect($url); } elsif ($cgi->path_info eq '/initiate') { my $request_token = $consumer->get_request_token( url => 'https://www.hatena.com/oauth/initiate', callback_url => $cgi->url.'/token', scope => 'read_public,write_public', ) or die $consumer->errstr."\n"; my $cookie = $cgi->cookie( -name => 'HATENA_OAUTH', -value => $request_token->as_encoded, -path => $cgi->url(-absolute=>1), ); my $url = $consumer->url_to_authorize( url => 'https://www.hatena.ne.jp/oauth/authorize', token => $request_token, ); print $cgi->redirect( -url => $url, -cookie => $cookie, ); exit; } elsif ($cgi->path_info eq '/token') { my $oauth_verifier = $cgi->param('oauth_verifier'); my $request_token = OAuth::Lite::Token->from_encoded( $cgi->cookie('HATENA_OAUTH')); my $access_token = $consumer->get_access_token( url => 'https://www.hatena.com/oauth/token', token => $request_token, verifier => $oauth_verifier, ); my $cookie = $cgi->cookie( -name => 'HATENA_OAUTH', -value => $access_token->as_encoded, -path => $cgi->url(-absolute=>1), -expires => '+14d', ); print $cgi->redirect( -url => $cgi->url.'/', -cookie => $cookie, ); } else { print $cgi->redirect($cgi->url.'/'); }
今回は Request Token と Access Token を Cooklie に保存してますが、これだとネットワークに流れてしまうので、いけないやり方です。