Google+ API with OAuth2.0

Google+APIを使うには、API keyを使う方法と、OAuth2.0で認証する方法があるらしい。

  • API key
    • 使うのが簡単
    • 認証が必要なAPIは使えない
  • OAuth2.0
    • 使うのが面倒くさい
    • 認証すればすべてのAPIが使える

OAuthの使い方については、こちらのサイトなどを参考にしました。

かなり細かく説明されているので、上のサイトを見ながらやっていけば分かるのであまり書くこともないのだけど、Net::Twitter風にアクセストークンを取得するコードを書くとこんな感じ。Client ID/SecretはGoogle APIs Consoleのページから作成します。

use strict;
use warnings;
use utf8;
use Encode;
use JSON::XS;
use LWP::UserAgent;
use HTTP::Request::Common qw/POST/;

my $auth_uri_base  = 'https://accounts.google.com/o/oauth2/auth';
my $token_uri_base = 'https://accounts.google.com/o/oauth2/token';
my $redirect_uri   = 'urn:ietf:wg:oauth:2.0:oob';
my $scope          = 'https://www.googleapis.com/auth/plus.me';
my $client_id      = '[Your Client ID]';
my $client_secret  = '[Your Client Secret]'

my $auth_uri = sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s", $auth_uri_base, $client_id, $redirect_uri, $scope);

print "Authorize this app and enter the PIN# from:\n", $auth_uri, "\n\nPIN = ";
my $pin = <STDIN>; # wait for input
$pin =~ tr/\x0A\x0D//d;

my $req = POST($token_uri_base, [
  client_id     => $client_id,
  client_secret => $client_secret,
  redirect_uri  => $redirect_uri,
  grant_type    => 'authorization_code',
  code          => $pin,
  ]);

my $ua  = LWP::UserAgent->new;
my $res = $ua->request($req);

if ($res->is_success) {
  my $coder = JSON::XS->new->utf8(1);
  my $hash  = $coder->decode($res->content);
  print "\n";
  foreach(keys %$hash){
    print "$_\t : $hash->{$_}\n";
  }
} else {
    print $res->status_line, "\n";
}

パラメーターのエスケープ処理をさぼってるけど、動いてるのでまあいいでしょう。

$ perl get_token_google.pl
Authorize this app and enter the PIN# from:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=[Your Client ID]&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/plus.me

PIN = [PIN code]

refresh_token    : [Refresh Token]
expires_in       : 3600
id_token         : [ID Token]
access_token     : [Access Token]
token_type       : Bearer

id_tokenとはなんぞや?と思って調べると、JWT(JSON Web Token)らしい。で、JWTってなんぞや?と思って調べると、webアプリケーションなどのserver-to-serverで使う、と書いてあるが、よく分からん。
Using OAuth 2.0 for Server to Server Applications  |  Google Identity Platform  |  Google Developers

とりあえずid_tokenは置いておいて、Perlからaccess_tokenを使うとこんな感じ。

use warnings;
use utf8;
use Encode;
use JSON::XS;
use LWP::UserAgent;
use HTTP::Request::Common qw/GET/;
use YAML::Syck;
$YAML::Syck::Headless = 1;
$YAML::Syck::SortKeys = 1;
$YAML::Syck::ImplicitUnicode = 1;

binmode STDOUT, ":encoding(utf8)";

my $id  = shift;
my $uri = "https://www.googleapis.com/plus/v1/people/$id";
my $access_token = '[Your Access Token]';

my $ua  = LWP::UserAgent->new;
my $req = GET($uri, (Authorization => "Bearer $access_token"));
print "Request :\n" . $req->as_string;

my $res = $ua->request($req);
print "Response :\n";
print Dump(JSON::XS->new->utf8(1)->decode($res->content))
$ perl people_get.pl 102808008463301583196
Request :
GET https://www.googleapis.com/plus/v1/people/102808008463301583196
Authorization: Bearer [Your Access Token]

Response :
aboutMe: 所属事務所/AKS  ニックネーム/さくら<br />
displayName: 宮脇咲良
etag: "\"seVFOlIgH91k2i-GrbizYfaw_AM/kyq92wdTx-ArgG8c6itrCUj24bw\""
gender: female
id: '102808008463301583196'
image:
  url: https://lh6.googleusercontent.com/-P-JDWJDjao8/AAAAAAAAAAI/AAAAAAADiJM/Bdh_SvtPMtY/photo.jpg?sz=50
isPlusUser: !!perl/scalar:JSON::XS::Boolean 1
kind: plus#person
name:
  familyName: 宮脇
  givenName: 咲良
objectType: person
tagline: HKT48
url: https://plus.google.com/102808008463301583196
urls:
  -
    label: HKT48公式サイト
    value: http://www.hkt48.jp/profile/sakura_miyawaki.html
  -
    label: Amebaブログ
    value: http://ameblo.jp/hkt48/
verified: !!perl/scalar:JSON::XS::Boolean 0

ほう。

面倒くさそうだなーと思っていたけど、単にユーザーとして使う分には意外と簡単だった。

あと、refresh_token の使い方がいまいち分からんかったのだけど、access_tokenは3600秒で期限切れになってしまうようなので、再びaccess_tokenを取り直す時に使うらしい。

$ curl -D - -H "Authorization: Bearer [Expired Access Token]"  https://www.googleapis.com/plus/v1/people/102808008463301583196
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="https://www.google.com/accounts/AuthSubRequest", error=invalid_token
Content-Type: application/json; charset=UTF-8
Date: Mon, 23 Sep 2013 00:14:54 GMT
Expires: Mon, 23 Sep 2013 00:14:54 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Server: GSE
Alternate-Protocol: 443:quic
Transfer-Encoding: chunked

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "authError",
    "message": "Invalid Credentials",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "code": 401,
  "message": "Invalid Credentials"
 }
}
$ curl -d refresh_token=[Refresh Token] -d client_id=[Client ID] -d client_secret=[Client Secret] -d grant_type=refresh_token https://accounts.google.com/o/oauth2/token
{
  "access_token" : "[New Access Token]",
  "token_type" : "Bearer",
  "expires_in" : 3600,
  "id_token" : "[ID Token]"
}
$ curl -H "Authorization: Bearer [New Access Token]"  https://www.googleapis.com/plus/v1/people/102808008463301583196
{
 "kind": "plus#person",
 "etag": "\"seVFOlIgH91k2i-GrbizYfaw_AM/kyq92wdTx-ArgG8c6itrCUj24bw\"",
 "gender": "female",
 "urls": [
  {
   "value": "http://www.hkt48.jp/profile/sakura_miyawaki.html",
   "label": "HKT48公式サイト"
  },
  {
   "value": "http://ameblo.jp/hkt48/",
   "label": "Amebaブログ"
  }
 ],
 "objectType": "person",
 "id": "102808008463301583196",
 "displayName": "宮脇咲良",
 "name": {
  "familyName": "宮脇",
  "givenName": "咲良"
 },
 "tagline": "HKT48",
 "aboutMe": "所属事務所/AKS  ニックネーム/さくら\u003cbr /\u003e",
 "url": "https://plus.google.com/102808008463301583196",
 "image": {
  "url": "https://lh6.googleusercontent.com/-P-JDWJDjao8/AAAAAAAAAAI/AAAAAAADiJM/Bdh_SvtPMtY/photo.jpg?sz=50"
 },
 "isPlusUser": true,
 "verified": false
}

やったね。

レスポンスコードが401ならtokenをrefreshする、という感じでOKみたい。

追記、というかこちらが本題なのですが

そもそもの動機としては、Google+からHKT48メンバーの発言を監視してtwitterにpostするスクリプトを作ったことでして。

簡単なのでAPI keyを使った実装でしばらく運用していたのですが、現時点では10分ごとに実行するとAPI keyの許容量の5〜6割、5分おきに実行すると限界を超えて使えなくなってしまう。最初気付かずに「全然更新されないなー」などと思ってた。

HKTメンバーが39人なので*1、5分毎に投稿を取得すると一日で39*24*60/5 = 11232、10kちょいになりますね。

Google+ API (Sign-in) - For methods that are allowed by the scope https://www.googleapis.com/auth/plus.login (people.get, people.list, moments.insert, moments.remove and moments.list)

Google+ API - For all other methods

https://developers.google.com/+/api/#quota

Sign-inの方が20,000,000 requests/day、その他が10,000 requests/day。なるほど、サインインすれば20M/day使えるのかーやったーと思っていたのだけど、上に書いてあるAPISign-inとして数えられるらしく、サインインしたところでその他のAPI(たとえばactivities.listとか)はその他として計上されるらしい。じゃあわざわざOAuth使う意味ないね、面倒なだけだし・・・。

もう複数アカウント、あるいは複数プロジェクトを作って回すしかないか・・・と思いつつ、ダメ元でquotaを増やすリクエストをしてみたところ、3日くらいでメールをいただきまして増やしてもらえた!やった!

とりあえず10倍に増やしてもらったので、当分は持ちそう。

画像はquotaが増えた後のものなので上限が10%になってるけど、当時はここで100%になってました。冷静に考えれば、5分レベルのリアルタイムさは必要としてなかったのだった。

ところで、twilogで統計情報取ると、彼女たちの生活リズムが丸分かりですごくいけないことをしている気分になりますな。

kkb_gp_bot(@kkb_gp_bot) Stats - Twilog

*1:秋吉優花ちゃんは年齢制限によりGoogle+を使えませんが