複数ドメインのtracking

Google AnalyticsのAPIの説明をしながら解説してみたい。

まず、Google Analytisはfirst party cookieを使ってデータを取得・送信している。first-party cookieとは、個々のドメインに対して機能するものなので、違うドメインのcookieとは、連携する事はできない。(行動ターゲティングなどは、画像一つ一つがドメインとなれる事を利用して、複数ドメインでのユーザーの行動追跡を可能にしている、たぶん)

とにかく、データ収集範囲は、ドメインに限定される。

(2010/03/07 cookieの値に関する限りです。google analyticsは、web property IDが同じなら、サイトが違っても、同じレポート上のデータとして扱う)

が、ドメインを超えて、データを収集したい。そこで、Google Analyticsでは、URLにデータを乗せて(post送信もURLにくっつく形)、遷移した別のドメインで、そのデータをga.jsが読み書きするになっている。

これを行うAPIは、_link, _getLinkerUrl , _linkByPost 3つ。同時に、_setAllowLinker(true)が、この3つが機能させるフラグ、trueで機能する、デフォルトはfalse。

また、_ga.jsの中で、ドメインの相違をcheckしているので、それを外すのが_setDomainName()。この引数には、デフォルトで “auto”(そのページのドメインが使われる) “none”(ドメインハッシュを1にする), または、自分で任意の文字列をドメインとして設定することもできるよう。

また、_setAllowHashというものもあり、引数をfalseにすると、ドメインハッシュを行わず、checkも行わない。よって、cookieのドメインcheckをせず、複数ドメインに対応可能になる。又、domain-hashをしないので、実行効率もあがるらしい。

サブドメインの間でデータを取る場合に、_setDomainName(“.sub.hoge.com”)のように、頭に ” . “ を書く事が推奨されているけど、この理由はよくわからない。これは、sub-domain共通でデータを取得してますというのを、わかりやすくするのが理由だろうか?
(2010/03/07 cookieは、.hoge.com のように書くと、サブドメイインでの値の共有になるとの事)

cookieの値の受け渡しができれば、ドメインハッシュ値を同じにして、ga.jsでのdomainチェックを通し、データを送信でき、複数ドメインでのデータを同じプロファイルで扱えることになる。

Iframeでも同様。Iframenも平行してアクセスがあると考える(ページ遷移などでレポートされる遷移の順番は、utmbの番号などを見れば、いいだろうか?)

また、注意するべき点として、setDomainNameは、できるだけ早い順番にかくべき。pageTracker変数を作った後に。_setVarなど(もう廃止項目だけど)を使う場合に、setDomainNameでドメインをそろえてないと、変更前のドメインとなるので、trackePageviewに反映されない。(_setVarの変更でリクエストは飛ぶけど、これも_setCustomVarだと飛ばないよう)なので、

  1. var pageTracker = _gat._getTracker(“UA-XXXXXX-11”)
  2. pageTracker._setDomainName(“none”);
  3. pageTracker._setAllowLinker(true);
  4. pageTracker._setVar(“hogehoge);
  5. pageTracker._trackPageview();

この順番で試したら問題なく、utmvの値も引き継げた。また、htmlの中のリンクに直接 pageTracker変数を使う(<a>タグのonclickなどで)なら、bodyの頭に、上記のコード(1-5)を入れる必要がある。

しかし、外部リンクにいちいち、pageTrackr._linkerなどと書いていくのは、実用上無理がある気がする。めんどくさい。

ので、下の記事にも書いたけど、一括でEventListenerに登録する方が漏れが少ないと思う。google analytics 外部へのリンクを計測する

closureの利用を考える

closure_memo_001
javascriptの高度なtopicといえば、closureだと思う。
理解が難しいと思う。カウンターを作ってみた時は感動したけど、なかなか理解が汎化していかない。それで、まとめを書いて見ることにした。
他にも、いろんなサイトを見て回った。
下のコードの例は、上の記事のものと全然違うものもあります。信頼性を求める人は上のリンクを辿って下さい。

setTimeoutでの関数の参照で使う

setTimeoutには、関数オブジェクトの参照か無名関数の定義式を入れる事になっている。setTimeout(ref_func_obj, seconds)

ただ、関数オブジェクトではなく、実行式を書きたい時は多い。fun()といった後ろに () が付く形。

そこで、引数をくるんで、引数なしの関数オブジェクトを返すようにする。org_funcが与えられていた時、引数を定義式の中に組み込んでしまう形のnew_funcを作り(参照させ)、それをsetTimeoutに使う。

Function.prototype.curry = function(arg){
  var self = this;
  return function(){
    self.apply(this, [arg]);
  };
};
var org_func = function(a){
  console.log(a);
};
new_func = org_func.curry("konitiwa");
function a(arg){
  setTimeout(new_func, 2000);
}

こんな感じかな?これをカリー化と言うのはまずいのだろうか?

Functionクラス(Functionオブジェクトのprototype)をOpen Class?するのは汚い方法かもしれないけど、しょうがない。

リンク先の記事では、オブジェクト、メソッド、メソッドへの代入式の3つをclosureして?新しい関数を作っている。

動的に作成したDomのイベントに関数を定義

動的にDomを作るconstructor関数を作ってやる。その時に、イベントでtriggerされる関数をつけてやりたい。でも、その関数の定義は外に出しときたい?(ここら辺、よく分かってない、、、)

オブジェクトとメソッド名を与えると、そのオブジェクト[メソッド名]という関数名(メソッド名)で、イベントと新規に作ったDomオブジェクトを引数に取る関数を作るようにする。

function associateObjWithEvent(obj, methodName){
  return function(e){
    e = e || window.event;
    return obj[methodName](e);
  };
}

これを、Domを作るconstructor関数に組み込む。thisのメソッドという形になる。

function DhtmlObject(elementType, id){
  var el = document.createElement(elementType);
  this.ele = el;
  el.id = id;
  el.innerHTML = "New Div";
  document.body.appendChild(el);
  if(el){
    el.onmouseover = associateObjWithEvent(this, "doMouseOver");
  }
}

そして、インスタンスメソッドとして、doMouseOverを定義すれば、この例だと、mouseoverでtriggerされるeventになる。

DhtmlObject.prototype.doMouseOver = function(event){
  console.log(event, this.ele);
};
//それで、動的にDomを作る(タグ、IDを指定)
new DhtmlObject("div", "divId");

これで、Domを作成する定義式と、イベントが起きた時に実行される関数を分離できた。作ったdomをglobal変数に入れて、ごにょごにょする必要もなくなった。(ここら辺りは、理解が怪しい)

ローカル変数の作成の重複を避ける

関数を呼び出すたびに、その関数のローカル変数を作るのはさけたい。よく例題に出るカウンターの例と同じタイプ。

closureにすれば、そのローカル変数(closureされる変数)は共通化される。

divの中にimgタグをいれる形のdocument.elementを作る。この時に、文字列をinnerHTMLに突っ込む形にする。それで、共通部分をローカル変数に、変わる部分はパラメータとする。

closureを使うべきでない場合

内部関数を作る事による速度低下に気をつける。closureを作ったけど、利用時に、その環境を使わないのなら、closure を作らず、外側で関数を定義しておいて、それをassignしてもよい。

constructor関数内で、this.method1 = function(){….}と定義して、closureを作るのは、呼び出し回数が少ない場合に限定する。そのconstructorが何回も使われるものであれば、AConstructor.prototype.method1 = function(){….}と、constuctor関数の外で、定義していく。

Googleカスタム検索(Web Element)でGAを使う(運用・改善編)

*この記事は、いつもに増して誤りが多いので、注意して読んで下さい。(しかし、Google Analytics上の定義は理解できないものが多い。みんな苦労してないのかな??)

Googleカスタム検索(Web Element)でGAを使う

前回のGoogleカスタム検索(Web Element)でGAを使う(設定編)の次は、運用(Google Analyticsを使った)になる。

Google Analyticsのレポート画面を確認する

以下は、このデータを自分でサイト内検索を表示した画面。

サイト内で、

  • 何回検索されたのか?
  • それはどのページなのか?
  • 検索した表示された記事がclickされ閲覧されたか
  • 検索した後の、離脱するまでの滞在時間、pageview
  • 再検索した回数
などが記録されている。
項目別に見ていく。
1. 利用状況
サイト内検索をしたセッションと、そうでないセッションをわけてくれる。仮に、サイト内検索した人を追跡したい場合は、前回の記事のutmvの使いどころ、流入元を追加的に記入するみたいなやり方で、cookieのutmvにサイト内検索をしたという値を追記すればいいと思う。(この記事を書きながら、僕のサイトにもそういう設定をした)
2.次の検索キーワード、開始ページ、到達ページ、カテゴリは、同じ指標(メトリックス)を見せてくれる。
表示されるメトリックス
  • 検索結果の平均ページビュー -  検索結果をクリックした
  • 検索結果の離脱率 - 検索直後に直帰した
  • 再検索 - 続けて検索した。
  • その後のページビュー – 検索後の平均ページビュー
検索回数と検索後の行動を記録してくれるという事。
一応、赤丸で囲んだように、ヘルプで説明があるけど、こんな意味だと思う。
をみる。ここの例のところを見ると、
検診結果の平均ページビュー(検索結果をclickした数検索後のpageview数/検索回数)
その後の平均ページビューは、(???/検索したセッションの総数)
???の部分は、検索後のページビューではなく、平均ページビューと同じように検索結果をclickした数に読めるのだが、、、両者の違いは、分母にある、検索回数と検索したセッション数の違いにみえる。僕のレポートに出てる数字も、実感としてはそのように見える。
ただ、最初の定義の部分は、単なるページビューに読める。(これでいいみたい ???はページビューで良いのか??)
下の図は、ディメンジョンが検索キーワード

検索キーワードは、本物?のディメンジョンなので、他のディメンジョンと切り替えが効く。

他の項目(開始ページなどは)は、正式にディメンジョンになってないので、切り替え表示できないのだと思うGoogle AnalyticsのAPIページを見ても、サイト内検索用のAPIはない。検索用のパラメータが途中で入ったら、サイト内検索をしたという処理なんだと想像する。
カテゴリは今回設定してないけど、プロファイルの設定画面で、クエリーパラメータを追加すればいい。
傾向は、時系列でのサイト内検索のデータ(上で書いた項目)を出してくれる。
以上が運用

それで、どうやってデータを生かすか?

設定して、数値を見て、それをどう生かすのかだけど、
自然検索の流入の時と同じ感覚しか思いつかない。
  • キーワードの発掘
  • そのキーワードから見えるニーズに対応したページをみせられているかどうか?
  • コンバージョンに誘導できているかどうか?
  • その他(他にもあるんだろうけど、思いつかない)

サイト内検索してくれた人にお願いする。

たぶん、サイト内検索までしてくれる人には、アンケートみたいなのを頼んでもいいのではないかと思う。
検索直後に出すと迷惑がられそうなので、結果が出ない場合にオンラインフォームを出してみる。
とりあえず、簡単にgoogle spreadsheetで、online-formを作ってみた。普通の検索結果と同じように、”x”を押すと消える。
ウェブページの顔の部分に、検索やアンケート記入を持って行くのは問題かもしれないけど、ユーザの利便性は高まるかもしれない。

Googleカスタム検索(Web Element)でGAを使う(設定編)

Googleカスタム検索(Web Element)でGAを使う

サイト内検索は便利な機能なので、今回、もう一度書く。

まず、Google Custom Search に行ってエンジンを作る。

基本情報を入れる。

名前、説明、キーワード、言語、翻訳、文字コード、検索範囲、広告、Subscribed Links(ユーザがこの

その後、対象サイトを含める。(複数のサイトを含める事もできる)

他にも、いろいろ設定項目があるけど、とりあえずGet codeでコードを取得

これを、サイト上で出現させたい場所に張る。僕の場合は、header部分にいれた。

次に、Google Analyticsでの設定。

新しい設定なので、新規にプロファイルを作っておいて、実験する形がいいと思う。

新しいプロファイルを、設定するドメインの上で作る。

既存のドメインにプロファイルを追加、プロファイル名は適当に。

プロファイルを作成したら、設定。

設定箇所は3カ所

サイト内検索を有効、クエリパラメータ(僕の場合はgにした),

クエリパラメータの削除

こちらの方法では、前の記事で説明したように、ajaxを使っていてURLがanalyticsで取得できない。

その対策上、urlをtrackPageviewで飛ばす形を取っている。

パラメータを削除しておかないと、検索開始ページにクエリが残ってしまい、閲覧開始ページがばらばらになってしまう。

(たぶん、Web Element以外のやり方なら大丈夫だと思う。)

下のscreenshotは、パラメータが残ってしまった場合。

後日直して、正常なデータも入った形。

これで、設定はとりあえずOK。

あとは、Googleカスタム検索(Web Element)でGAを使う(運用・改善編)

20090915-20091015アクセス解析

このサイトabc-analytics.comと、wiki.slash-reader.comのデータを見てみる。

最終的にどういうactionに落とす事ができるか不明だけど、それを意識して進める。

キーワードに的を絞る。

検索流入数(左がwiki.slash-reader.com(wiki), 右がabc-analytics.com(wp))

2009年9月15日火曜日,4,0
2009年9月16日水曜日,6,0
2009年9月17日木曜日,2,0
2009年9月18日金曜日,1,1
2009年9月19日土曜日,1,0
2009年9月20日日曜日,3,0
2009年9月21日月曜日,1,0
2009年9月22日火曜日,6,0
2009年9月23日水曜日,3,3
2009年9月24日木曜日,8,2
2009年9月25日金曜日,9,1
2009年9月26日土曜日,3,0
2009年9月27日日曜日,2,0
2009年9月28日月曜日,6,1
2009年9月29日火曜日,10,5
2009年9月30日水曜日,7,0
2009年10月1日木曜日,6,6
2009年10月2日金曜日,13,3
2009年10月3日土曜日,2,2
2009年10月4日日曜日,4,1
2009年10月5日月曜日,12,3
2009年10月6日火曜日,9,5
2009年10月7日水曜日,6 ,9
2009年10月8日木曜日,6 ,3
2009年10月9日金曜日,1,6
2009年10月10日土曜日,0,6
2009年10月11日日曜日,2,3
2009年10月12日月曜日,4,2
2009年10月13日火曜日,12,8
2009年10月14日水曜日,6,4
2009年10月15日木曜日,10,12

キーワードの種類(wiki:116, wp:87)、セッション数は(wiki:165, wp:86)。

wpの方は、セッション数= keywordの種類。競合がないキーワードの組み合わせでの流入になっている。(検索ボリュームが非常に少ない検索フレーズを示している、と思う)

ちなみに、google にindexされるようになって検索にかかるようになったばかり。サイトの年齢が若くても、そんなにハンディが無い印象。被リンクをもらう事が大事。

ちなみにwordpressだと、yahooは上手くindexしてくれないよう。現在、1記事だけindexされてるだけ。その後、全然indexしてくれない。企業の人がwordpressでやるときは、この辺りどうするのだろう???(樂天ビジネスなんかを見ると、時々wordpress案件があるし、他にもwordpressサイトはたくさんある)

検索されたキーワードは、複合検索が多く、非常に小さな検索ボリューム(下で分析する)。ビジネス上のコンバージョンを考えないのであれば、long tailをこつこつ積み上げれば、アクセスは集められるような気がするが、金銭的な価値回収が難しい。このような価値を回収できるアマゾンのビジネスのすごさが、いまさらながら理解できた気がする。

こん検索もあった。

intitle:変動 | intitle:変更 | intitle:seo | intitle:sem intitle:google or intitle:yahoo

なるほど、こうやって検索するのか、勉強になる。

個別には、マイナスキーワードでの検索もあった。使った事がないけど、使わないと検索生活の質が落ちるのかもしれない。

csvに落として、rubyでこちょこちょやる。

昔、どっかで見た、csvファイルを読んで、そのまま列単位でオブジェクトにしてくれるscritpを改変したものを使う(csvのactive-record版みたいなイメージ) ちなみに、ファイルはこんな感じ。場合によっては、エクセルより手軽になる。

さて、流入キーワードを見ていく。

複合検索が以外に多い。

[単語数: 流入回数]を見る。

wp:   [[1, 11], [2, 29], [3, 28], [4, 11], [5, 8]]

wiki: [[1, 7],  [2, 23], [3, 55], [4, 20], [5, 7], [6, 3], [10, 1]]

(10wordsはさっき上での検索タイプ)

中心値は、3 words辺り。まあ、それぐらいにニッチにならないと、検索で上位に出てこないからだろう。

でも、5 wordsくらいで検索する人もかなりいるという事だ。

見た目のシンプルさにこだわるgoogleが 検索窓をあえて横長にするわけだ。

(参考記事: 8語以上の複合キーワード検索が伸びている)

求めてる情報を単語単位で見てみる。

サイトのテーマの単語(google sites, anallytics)と付随してくる単語を取り出す。

まず、関係なさそうなもの(テーマ単語がないもの)を切る。(先ほど、mycsv.rbのファイルから)

obj.map(&:keywords).select{|e| e =~ /google|site/}.map{|e| e.gsub(/google|site|sites/, “”)}

切った後に、テーマ単語を消して表示する。

その後、回数は計って順列表示

wiki:(一回はのぞく)

[“apps”, 18],
[“api”, 15],
[“docs”, 5],
[“サイト”, 5],
[“gdata”, 5],
[“analytics”, 5],
[“連携”, 4],
[“favicon”, 4],
[“css”, 4],
[“コピー”, 4],
[“商用”, 3],
[“wiki”, 3],
[“問題点”, 3],
[“目次”, 2],
[“utmz”, 2],
[“xml”, 2],
[“script”, 2],
[“doc”, 2],
[“使い方”, 2],
[“cookie”, 2],
[“data”, 2],
[“キャビネット”, 2],
[“html”, 2],
[“ページ”, 2],
[“list”, 2],
[“送信”, 2],
[“atom”, 2],
[“活用”, 2]]

wp(analytics)

[“python”, 8],
[“trackpageview”, 5],
[“form”, 4],
[“api”, 4],
[“data”, 4],
[“アナリティクス”, 4],
[“-trackevent”, 3],
[“gdata”, 3],
[“計測”, 3],
[“getaccountlist”, 3],
[“gdata..service”, 2],
[“import”, 2],
[“ドメイン”, 2],
[“wordpress”, 2],
[“dataservice”, 2],
[“sites”, 2],

みごとに、当初自分の想定しなかった単語が(テーマの付属語として)最上位に来た。

被リンクがあったからだろう。結局、被リンクされるかどうかが分かれ目。

いずれにせよ、数が少ないので、何とも言えないけど、api関連は競合も少ないのだろう。

今度は、検索キーワード単位から、それをばらした単語単位にして、滞在時間の総計順を出す。

キーワード[ sessions, pageviews, stayonsite]という表記。

wiki

  • app(s)  [25, 76, 5395]
  • api  [19, 29, 3291]
  • docs [8, 45, 1615]

wp

  • python [5, 16, 1422]
  • trackpageview[1,1,653]
  • api [4, 12, 1692]

とりあえず、次にどういう行為をして欲しいという事はこれではわからない。

うーん、、

再)utmvの使いどころ、流入元を追加的に記入する

試行錯誤日記になってきた。こんな記事のコードをコピペする人はいないと思うけど、、書き直しが多い。

前の記事で、GAのcookieのutmvの値に追加的に流入元の値を入れる記事を書いたけど、いろいろ訂正する。

まず、下に貼り付けたコードは、まずかった。pageTracker._setVar(“hogehoge”)を送れば、同時にpageviewも送られると思って、setVarの時には、trackPageviewを送らないようにしてたけど、送らないといけない。

今日データを見たら、セッション数 > ページビュー という結果になっていた。

image 

_setVarが送られているので、sessionはカウントされているのだけど、trackPageviewがないので、ページビューがカウントされてない結果になった。、、、と思う。

もう一つ、ユーザ定義にセットした結果で、とりあえず新規のreferrer を突っ込んだけど、データを整理した形で格納したくなった。

image

 

訂正版だけど、まだまだ問題がありそう。googleとyahooの検索の時は、referrerのままでなく、キーワードを設定するようにした。(あと、exeをexecXとdreamhostの文字列の禁止制限に掛からないようにした。)

 


timerID1 = setInterval(function(){
                        if(_gat){
                          init_ga();
                          clearInterval(timerID1);
                          timerID1 = null;
                        }
                      },500);
timerID2 = setInterval(function(){
                        if(document && jQuery && _gat){
                          get_outbouund_link();
                          clearInterval(timerID2);
                          timerID2 = null;
                        }
                      },500);

//referrerや、profileページを見た場合には、superSetVarする。yahoo,googleからの来訪はキーワード

function init_ga(){
  window.pageTracker = _gat._getTracker("UA-188512-12");
  var d = new Date();
  d = d.getFullYear().toString()+d.getMonth().toString()+d.getDate().toString();
  var f = function(){
    var c = 1;
    return function(){
      pageTracker._trackEvent("time", c.toString(), document.location.href,2);
      c++;
    };
  }();
  setInterval(f, 10000);
  if(document.location.pathname == "/profile"){
        superSetVar("path:seen_profile;");
  }
  if(!!document.referrer && !document.referrer.match(/^https?://wp.slash-reader.com/)){
    var value;
    var r;
    try{
      if(r = document.referrer.match(/[&?]q=([^&$]+)/)){
        value = "key:" + r[1].replace(/+/g, "|") +  ";";
      }else if(r = document.referrer.match(/[&?]p=([^&$]+)/)){
        value = "key:" + r[1].replace(/+/g, "|") + ";";
      }else{
        r = document.referrer.match(/https?://([^/$]*)/);
        value = "ref:" + r[1] + ";" ;
      }
    }catch(e){
      value = "error:" + e.toString() + ";" ;
    }
    superSetVar(value);
  }else{
    pageTracker._trackPageview();
  }
}


//外部リンクへのイベントに、trackPageviewイベントをhookする
function get_outbouund_link(){
  try{
    as = jQuery("div.entry-content a");
    var ret = [];
    for(var i=0,L=as.length;i<L;i++){
      if(!as[i].href.match(document.location.host)){
        ret.push(as[i]);
      }
    }
    for(var i=0,L=ret.length;i<L;i++){
      jQuery(ret[i]).bind("click",
                     function(){
                       pageTracker._trackPageview("/virtual/" + this.href);
                       pageTracker._trackEvent("eternal_link", "getout", this.href, 2);
                     }
                    );
    }
    return true;
  }catch(err){
    //console.log(err);
    return false;
  }
}



// I(abc-analytics.com) copied from http://www.lunametrics.com/blog/2008/04/17/stuff-more-than-one-value-
in-gas-user-defined-segment/
// "standard" read cookie function  (copied from Prusak's gwo_write.js)
function read_cookie(cookie_name) {
  var my_cookie=""+document.cookie;
  var ind=my_cookie.indexOf(cookie_name);
  if (ind==-1 || cookie_name=="") return "";
  var ind1=my_cookie.indexOf(';',ind);
  if (ind1==-1) ind1=my_cookie.length;
  return unescape(my_cookie.substring(ind+cookie_name.length+1,ind1));
}

function superSetVar(appendValue) {
  var getVar = read_cookie('__utmv');       // read the __utmv cookie
  hasValue = getVar.indexOf(appendValue);   // does the cookie already have the value we want to append?
  if ( hasValue == -1 ) {                   // if the new value is not already in the cookie
    removePrefix = /^.*.(.*)/.execX(getVar);  // __utmv cookie has the format 12345678.cookieValue - remove
 the ## prefix
    if (removePrefix && removePrefix[1]) {
      newVar = removePrefix[1] + appendValue; // append the value
    }else{
      newVar = appendValue;                 // this will execute if the cookie was not already set.
    }
    //var superSetVarTracker = _gat._getTracker("UA-1-1");  // set up a tracker to call _setVar from
    //superSetVarTracker._initData();
    pageTracker._setVar(newVar);                   // call _setVar with the new value
    pageTracker._trackPageview();
  }else{
    pageTracker._trackPageview();
  }
}
function unSetVar(removeValue){
  var getVar = read_cookie('__utmv');         // read the __utmv cookie
  hasValue = getVar.indexOf(removeValue);     // does the cookie have the value we want to remove?
  if ( hasValue != -1 ) {                     // if the value is in the cookie then . . .  otherwise, there
 is nothing further to do.
    removePrefix = /^.*.(.*)/.execX(getVar);  // __utmv cookie has the format 12345678.cookieValue - remove
 the ## prefix
    if (removePrefix && removePrefix[1]) {    // if we remove the prefix then. . .
      //
      var re = removeValue + '[^/]*';
      re = new RegExp(re,'g');
      newVar = removePrefix[1].replace(re,""); // newVar = removePrefix[1] MINUS removeValue
      //
      //var superSetVarTracker = _gat._getTracker("UA-1-1");  // set up a tracker to call _setVar from
//    superSetVarTracker._initData();
      pageTracker._setVar(newVar);                   // call _setVar with the new value
      pageTracker._trackPageview();
    }

  }
}


utmvの使いどころ

Google Analticsのお勧め機能として、アドバンスドセグメントがある。計測されたセッションをいろんな属性の持つデータ(数字なら以上や以下、文字列なら正規表現まで)で分類して、レポートに反映させられる。解説本とかだと、この説明が中心だったりもする。

ただ、その属性はGoogleが予め用意したものである(Eg: 新規セッション、訪問回数、検索キーワードとか、ディメンジョンやメトリクスにあるもの)。その中で一つ、APIで自分の手で属性の値を設定できるものがある。それが、ユーザ定義、utmvの値。

これは、javascript で、pageTracker._setVar(“hogehoge”); と代入することで、__utmvクッキーの値をsetするもの。

これで、レポート側では、その値が反映される。

image

その後、アドバンスドセグメントに追加すれば、

image

ユーザ属性を対比して、actionable な解析ができるかもしれない。

前置きが長くなったけど、

ユーザ定義にはどのような値をセットしていけばいいのだろう?

1. 他のサイトの様子を見る。

自分のブラウザのcookieの__utmvを見てみると、login, logout,member,guest, experiments, ホスト名(referrer?), 使用言語、これらの組み合わせ、などなど、、など。

会員制のサイトなどでは、loginしてるかどうかをみてるようだ。(loginしてしまえば、あとは別の解析ツールの出番なのかもしれない)。ホスト名(referrer?)を記録しているところは、campaignの最初の値をsetしているのかもしれない。utmzで記録するcampainでは、直前の流入元しか原則的に記録しないので、その対策かもしれない。

2009/11/23 utmzの話でした。混同して書いてしまいました。ごめんなさい。
* nooverride=1を付けて流入した場合は、utmvのsourceは更新されない。(しかし、他人のリンクURLを変更などできない。自分の管理するものにしか適用できない)

2. 一つの方法として、ユーザ定義を外部流入計測に利用する

上で書いたように、Google Analytisでの campaignのcreditは直前の流入元に与えられ、それまでの値は消去される。(utmzの話)

だが、サイトの目標として設定するコンバージョンの多くは、複数回の訪問で起こる事が多く、複数の流入元を経由する可能性がある。google analyticsの仕様では計測できない。

で、google analyticsの良い記事が載っているサイトで Stuff More Than One Value into GA’s User Defined Segmentという記事を見た。(下のコードでは、ここのコードもそのままコピペしてる)

ここでは、utmvの値を積み上げる方式を提案してる(古い記事(2008/04)なので、今は違う方式があるのかもしれないが、、、)

とりあえず、これをまねした簡単な例として、referrerを積み上げる方式をやってみる。上記の記事のコメントのやりとりの中ででているもの

  1. utmvがundefinedなら、referrerをいれる。
  2. utmvの値にreferrerが含まれてなければ、referrerを加える。
  3. utmvの値にreferrerがあれば、そのまま。

これを発展させていくと、utmzの処理と似たものになっていく(当たり前か、、campaignの処理をしてるのだから)

なので、utmzの値(campaignの訪問回数)もいれると、より良いデータになるけど、余裕のある時にやることにする。

以前のgoogle analytics 外部へのリンクを計測する | Google,アクセス解析のコードと合わせて、コードを下に書く。ユーザ定義のデータは汚くなるし、ブラウザ上のレポートでは、固まった情報として見られないかもしれないけど、apiなりexcelへのコピペで見ることはできるようになると思う。

元のコードから、dummyで trackerを作る部分ののぞいた。superSetVarTracker = _gat._getTracker(“UA-1-1”)の部分が元のコードにはある。あとコード貼り付け時に、execをEXECCと書き直した。(ここのレンタルサーバの制限による)

timerID = setInterval(function(){
if(document && jQuery && _gat){
//                        console.log("start");
get_outbouund_link();
init_ga();
clearInterval(timerID);
timerID = null;
}
},500);

//referrerや、profileページを見た場合には、superSetVarする。それ以外は通常の_trackPageview
function init_ga(){
window.pageTracker = _gat._getTracker("UA-188512-12");
if(!!document.referrer && !document.referrer.match(/^https?://wp.slash-reader.com/)){
superSetVar(document.referrer);
}else if(document.location.pathname == "/profile"){
superSetVar("seen_profile");
}else{
pageTracker._trackPageview();
}
}

//外部リンクへのイベントに、trackPageviewイベントをhookする
function get_outbouund_link(){
try{
as = jQuery("div.entry-content a");
var ret = [];
for(var i=0,L=as.length;i
if(!as[i].href.match(document.location.host)){
ret.push(as[i]);
}
}
for(var i=0,L=ret.length;i
jQuery(ret[i]).bind("click",
function(){
pageTracker._trackPageview("/virtual/" + this.href);
pageTracker._trackEvent("eternal_link", "getout", this.href, 2);
}
);
}
return true;
}catch(err){
//console.log(err);
return false;
}
}

// I(abc-analytics.com) copied the code below from http://www.lunametrics.com/blog/2008/04/17/stuff-more-than-one-value-in-gas-user-defined-segment/
// "standard" read cookie function  (copied from Prusak's gwo_write.js)
function read_cookie(cookie_name) {
var my_cookie=""+document.cookie;
var ind=my_cookie.indexOf(cookie_name);
if (ind==-1 || cookie_name=="") return "";
var ind1=my_cookie.indexOf(';',ind);
if (ind1==-1) ind1=my_cookie.length;
return unescape(my_cookie.substring(ind+cookie_name.length+1,ind1));
}

function superSetVar(appendValue) {
var getVar = read_cookie('__utmv');       // read the __utmv cookie
hasValue = getVar.indexOf(appendValue);   // does the cookie already have the value we want to append?
if ( hasValue == -1 ) {                   // if the new value is not already in the cookie
removePrefix = /^.*.(.*)/.EXECC(getVar);  //  __utmv cookie has the format 12345678.cookieValue - remove the ## prefix
if (removePrefix && removePrefix[1]) {
newVar = removePrefix[1] + appendValue; // append the value
}else{
newVar = appendValue;                 // this will execute if the cookie was not already set.
}
pageTracker._setVar(newVar);                   // call _setVar with the new value
}else{
pageTracker._trackPageview();
}
}

function unSetVar(removeValue){
var getVar = read_cookie('__utmv');         // read the __utmv cookie
hasValue = getVar.indexOf(removeValue);     // does the cookie have the value we want to remove?
if ( hasValue != -1 ) {                     // if the value is in the cookie then . . .  otherwise, there is nothing further to do.
removePrefix = /^.*.(.*)/.EXECC(getVar);  // __utmv cookie has the format 12345678.cookieValue - remove the ## prefix
if (removePrefix && removePrefix[1]) {    // if we remove the prefix then. . .
//
var re = removeValue + '[^/]*';
re = new RegExp(re,'g');
newVar = removePrefix[1].replace(re,""); // newVar = removePrefix[1] MINUS removeValue
superSetVarTracker._setVar(newVar);                   // call _setVar with the new value
}

}
}