とある仕事でページビューに基づくランキングを作る必要があって実装方法を調査していたところ、Google Analytics のデータアクセス用のAPIがあったことを思いだし、実装してみた記録。
正直、直前までログファイルを自前で集計する方法しか考えてなかったけど、この方法を思いついて面倒な集計の手間がなくなって助かった。
手順としては、まず認証を行い、認証トークンを利用してデータ取得する流れ。認証についてはいくつか選択肢があるが今回は ClientLogin を利用(ソース中のget_auth)。認証トークンが正常に取得できたら後は Data Export API をコール(ソース中のget_ranking)すればXML形式で返答があるので、必要なデータのみ取り出して HTML として出力して終了。
結果は以下の通り。
Perlでの実装は以下のとおり。ちょっと冗長な気がするが、まいっか。
#!/usr/bin/perl use strict; use warnings; use Config::Pit; use DateTime; use LWP::UserAgent; use XML::Simple; my $config = pit_get( 'google.com', require => { 'table_id' => 'table id of google analytics', 'email' => 'your email on google.com', 'password' => 'your password on google.com', } ); my $ua = LWP::UserAgent->new; my $now = DateTime->now->set_time_zone('Asia/Tokyo'); my ( $start_date, $end_date ) = ( $now->clone->subtract( days => 8 )->ymd, $now->clone->subtract( days => 1 )->ymd ); my $token = get_auth( $ua, $config->{email}, $config->{password} ); my $ranking = get_ranking( $ua, $config->{table_id}, $token, $start_date, $end_date ); print <<__HTML__;__HTML__ exit; sub get_auth { my ( $ua, $email, $password ) = @_; my $res = $ua->post( 'https://www.google.com/accounts/ClientLogin', { Email => $email, Passwd => $password, accountType => 'GOOGLE', service => 'analytics', source => '5net-getRanking-01', } ); die $res->code . ':' . $res->message if $res->code != 200; if ( $res->content =~ /Auth=([^\s]+)/ ) { return $1; } else { die 'Could not get auth token.'; } } sub get_ranking { my ( $ua, $table_id, $token, $start_date, $end_date ) = @_; my $res = $ua->get( 'https://www.google.com/analytics/feeds/data?' . "ids=$table_id&start-date=$start_date&end-date=$end_date&" . 'dimensions=ga:pagePath&metrics=ga:pageviews&sort=-ga:pageviews&' . 'max-results=11', Authorization => "GoogleLogin Auth=$token", GData_Version => 2, ); die $res->code . ' : ' . $res->message if $res->code != 200; my $ref = XMLin( $res->content ); my @paths; while ( my ( $key, $val ) = each( %{ $ref->{entry} } ) ) { push( @paths, { pv => $val->{'dxp:metric'}->{value}, path => $val->{'dxp:dimension'}->{value}, }, ); } return [ sort { $b->{pv} <=> $a->{pv} } @paths ]; } sub get_title { my ( $ua, $path ) = @_; my $res = $ua->get( 'http://5net.com' . $path ); die $res->code . ' : ' . $res->message if $res->code != 200; if ( $res->content =~ m{<title>(.*?)</title>} ) { return $1; } else { die 'Could not get title.'; } }