Note: この記事は、3年以上前に書かれています。Webの進化は速い!情報の正確性は自己責任で判断してください。
Google Analytics(以下、GoAn)はimg要素を読み込むことリクエストを送信してて統計にカウントしてたから、同じようなURLを生成して送信すれば携帯サイトでもGoAnは利用できる。ただしCookieに値を保存できない携帯版だと幾つかの制約があって、簡単に言うと「全アクセスがユニーク扱いになる」。ここまでが前回のお話。
で、その後いろいろ試してみた結果。だいぶデータが見れるようになったので、公開してみる。統計に反映されるようになったデータを以下に記します。
- 滞在時間、滞在中のページビュー、直帰率
- リピート数、ブラウザ、OS、ユーザ定義
- 参照サイト、参照元、検索エンジン、キーワード
幾つか怪しいデータもあるけど、まあ仕組みが解ってれば十分参考にはなるし。この手の統計ってのは「ページビュー○○万件です!」て主張するより、増減の傾向や存在証明にこそ意味があるもんだ。数字なんて飾りです。スーツな人にはそれが解らんのですよ。解ってて利用するのが真のスーツだ。
ともあれ。
ライバルはうごくひとだ! ってことで、こないだ起源が判明したらしいSugamo.cssの3回目に持って行きました。
Cookies info
ソースを晒す前に中身のまとめ。GoAnを利用しているサイトでCookieを見てみると、以下のようになっている。まずブクマから閲覧した場合(Urchin.js)。
Name | Value |
---|---|
__utma | 169191153.1496068014.1241010801.1241067869.1241195482.4 |
__utmb | 169191153 |
__utmc | 169191153 |
__utmz | 169191153.1241010801.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none) |
次に、Googleから「Analytics 携帯」などと検索した結果ページから遷移した場合。
Name | Value |
---|---|
__utma | 169191153.1496068014.1241010801.1241067869.1241195482.4 |
__utmb | 169191153 |
__utmc | 169191153 |
__utmz | 169191153.1241196701.4.2.utmccn=(organic)|utmcsr=google|utmctr=Analytics%E3%80%80%E6%90%BA%E5%B8%AF|utmcmd=organic |
utmzの値の変化が判るだろうか。この2つの例と、The Chatterboxのてきとー和訳をもとに、それぞれの値の意味を掘り下げてみる。ここを作って送る訳だから、知ってるのと知らないのとでは天地の違いよ!
Google Analytics Cookies
Name | Description | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
__utma |
|
||||||||||||||||
__utmb |
|
||||||||||||||||
__utmc |
|
||||||||||||||||
__utmz |
|
||||||||||||||||
__utmv |
|
ドメインハッシュの生成方法は判らなかったのだけど、どのみち初回はJS埋め込んでアクティベートしてやる必要があるので、そのJSが生成したCoolie情報をPCブラウザで見てやれば問題なく取得できる。
サンプル
というわけで、ここまでの調査結果をもとに前回のソースを編集すると、以下のような関数ができあがりました。長いよ?
package Util;
use strict;
use LWP::UserAgent;
sub GoogleAnalystics {
my $session = $_[0];
#setting
# - host_name from target-host
# - domainH from data of Domain-Cookies
# - utmac from activate GoAn-data
my $host_name = 'archiva.jp';
my $domainH = '169191153';
my $utmac = "UA-xxxxxx-x";
#end setting
my $utmhn = "http://$host_name/";
my $utmn = int(rand(9999999998)) + 1; #random number
my $today = time();
my $utmp = $ENV{'REQUEST_URI'};
my $utmcs = 'Shift-JIS'; #character_coding
my $utmsr = '-'; #1024x768
my $utmsc = '-'; #32-bit
my $utmul = 'ja'; #en
my $utmje = '0'; #java_enable / 1
my $utmfl = '-'; #flash_version / 9.0%20r28
my $utmdt = '-'; #page title
#remove session-id from requestURL
my $session_id = $session->{_session_id};
if($session_id) {
$utmp =~ s{/$session_id/?}{}xms;
$utmp =~ s{\A([^/])}{/$1}xms;
}
#check UA
my $agent;
if ($ENV{HTTP_USER_AGENT} =~ m/Baiduspider | Yahoo! \s+ Slurp | Googlebot/xms){
return 1;
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/DoCoMo/xms) {
$agent = 'DoCoMo';
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/Vodafone/xms) {
$agent = 'Vodafone';
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/SoftBank/xms) {
$agent = 'SoftBank';
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/\A(?: UP.Browser | KDDI )/xms) {
$agent = 'au';
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/iPhone;/xms) {
$agent = 'iPhone'
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/emobile/ixms) {
$agent = 'EMobile';
}
elsif ($ENV{HTTP_USER_AGENT} =~ m/willcom/ixms) {
$agent = 'Willcom';
}
else {
$agent = $ENV{HTTP_USER_AGENT} if $ENV{HTTP_USER_AGENT};
}
#check session
my $visitor_id = $session->retrieve('visitor_id');
my $first_time = $session->retrieve('first_time');
my $current_time = $session->retrieve('current_time');
my $last_time = $session->retrieve('last_time');
unless ($visitor_id || $first_time || $current_time || $last_time) {
$visitor_id = int(rand(2147483646)) + 1; #number under 2147483647
$first_time = time();
$current_time = time();
$last_time = time();
}
#check referer
my $referer = $ENV{'HTTP_REFERER'};
my $utmccn = 'utmccn%3D(direct)';
my $utmcsr = '%7Cutmcsr%3D(direct)';
my $utmcmd = '%7Cutmcmd%3D(none)';
my $utmctr;
if ( $referer =~ m{http://}xms ) {
#strip host-name
($utmcsr = $referer) =~ s{\A http:// ([^/]*) .* \z}{$1}xms;
$utmcsr = '%7Cutmcsr%3D'.$utmcsr;
if($utmcsr =~ m{$host_name}xms) {
$utmcsr = '%7Cutmcsr%3D(direct)';
}
elsif ($referer =~ m{ezsch.ezweb.ne.jp}xms ){
($utmctr = $referer) =~ s{\A .+ query= ([^&]*) .* \z}{$1}xms;
}
elsif ($referer =~ m{search.mobile.yahoo.co.jp}xms ){
($utmctr = $referer) =~ s{\A .+ p= ([^&]*) .* \z}{$1}xms;
}
elsif ($referer =~ m{www.google.co.jp/m/}xms ){
($utmctr = $referer) =~ s{\A .+ q= ([^&]*) .* \z}{$1}xms;
}
else {
$utmccn = 'utmccn%3D(referral)';
$utmcmd = '%7Cutmcmd%3Dreferral';
warn 'Referer: '.$referer;
}
$utmccn = 'utmccn%3D(organic)' if $utmctr;
$utmctr = '%7Cutmctr%3D'.$utmctr if $utmctr;
$utmcmd = '%7Cutmcmd%3Dorganic' if $utmctr;
}
###############################################
#utmcc = cookiesettings
my $urchinUrl = 'http://www.google-analytics.com/__utm.gif?utmwv=1'
."&utmn=$utmn"
."&utmcs=$utmcs"
."&utmsr=$utmsr"
."&utmsc=$utmsc"
."&utmul=$utmul"
."&utmje=$utmje"
."&utmfl=$utmfl"
."&utmdt=$utmdt"
."&utmhn=$utmhn"
."&utmr=$ENV{'HTTP_REFERER'}"
."&utmp=$utmp"
."&utmac=$utmac"
.'&utmcc=__utma%3D'.$domainH.'.'.$visitor_id.'.'.$first_time.'.'.$last_time.'.'.$current_time.'.2'
.'%3B%2B__utmb%3D'.$domainH
.'%3B%2B__utmc%3D'.$domainH
.'%3B%2B__utmz%3D'.$domainH.'.'.$last_time.'.2.2.'.$utmccn.$utmcsr.$utmctr.$utmcmd
.'%3B%2B__utmv%3D'.$domainH.'.'.$visitor_id.'%3B'
;
my $ua = LWP::UserAgent->new;
my $request = HTTP::Request->new(GET => $urchinUrl);
if ($ENV{'HTTP_ACCEPT_LANGUAGE'}) {
$request->header('Accept-language' => $ENV{'HTTP_ACCEPT_LANGUAGE'});
}
if ($agent) {
$ua->agent($agent);
}
my $response = $ua->request($request);
return 1;
}
1;
で、current_time等を更新してから初回だけタイムスタンプ類をセットして呼び出してやります。例としてはそこらも書いてやるべきなんかもしれませんが、セッション管理の方法は各々違うでしょうから、省略! 流れとしては...
- 主要な変数を設定(ホスト名、ドメインハッシュなど)
- URLからセッションIDを消す(セッションIDをURLに含めている場合)
- UAをセット
- 訪問時間をセット
- 参照元をセット
- URLを作って、送信!
...というような感じ。
UAのIF分岐は別にやらなくても良い。むしろ素のUAのままのほうが、「OS」等の解析結果が利用できるようになります。上記サンプルではちょっと詳しくキャリア別にカウントしたかったのと、そのまま検索botがDoCoMoに含まれることもあったため、自前で文字列作って渡してます。
検索キーワードの抽出は参照元の部分でやってます。検索エンジンごとにクエリ文字列の分解とかやってるのは泥臭く感じますが、Urchin.js見てみると、本家GoAnでも50近いサイトを登録して個別にやっているようですし、まあ致し方ないでしょう。数が少ないのは、ちょこっと運用していくうちに増やすとして...
いまんとこ、こんな感じです。
課題
参照元でドメインまでは出ても、そこから個別ページのURLが表示されないです。たぶんここまで空気だったutmrとの連携なんじゃないかなあ? まあドメインからログ検索すれば簡単に判るんですが、なんか悔しい...
あと、ページタイトルだけはどうしても取れない... これは実行タイミングの問題。うちのシステムだとテンプレ出力の前に実行しちゃうから、各ページタイトルを取得するのは難しいんだよなあ。テキストファイルとかから読み出すと重くなっちゃうし。DBアクセスは負けだと思ってる。
Note: スパム対策が面倒なので、コメント投稿を廃止しました。以前のコメントは残します。
ご意見・ご要望はtwitter@sigwygかはてブコメントにて。