システム改修をせずにGoogleアナリティクスのeコマーストラッキングを実装する

eコマースのシステム(ショッピングカート)においてGoogleアナリティクスの
Eコマーストラッキングを実装する場合、通常はトラッキングする要素に動的な要素を含む、
システムが絡むため、システム改修が必要ということになる。

しかしシステム改修となると開発費もかかるため、なかなか踏み出せない。
またeコマースのシステムによっては商品名や価格を動的に出すなどプログラムのかかわる部分には
一切手を加えられない場合もある。

そこでJavascriptだけで注文番号、商品名、金額などを取得する方法をここでは紹介する。
つまりタグマネージャー一つ設置されていれば、そのカスタムタグ機能で
eコマーストラッキングに必要な情報をすべて取得してGoogleに送信するのである。

仮にタグマネージャーが設置されていない場合でも、
システムの動的な部分に手を加えないため最小コストで実装することができる。

またこの方法はGoogleアナリティクスに限らず、他のシステムでのEコマース用の
コンバージョントラッキング(どの商品を注文した、購入金額はいくらか)でも応用できる。

なおここで紹介するのは拡張eコマースではなく従来のeコマーストラッキングの実装方法である。

eコマーストラッキングの実装方法のおさらい

プラグインの読み込み

トラッカーを生成した後(ga('create')した後)で

ga('require', 'ecommerce');

トランザクションの追加

上記でプラグインを読み込んだ後で

ga('ecommerce:addTransaction', {
  'id': '1234',                     // トランザクションID
  'affiliation': 'Acme Clothing',   // 店舗名やアフィリエイト名
  'revenue': '11.99',               // 購入総額
  'shipping': '5',                  // 送料
  'tax': '1.29'                     // 税金
});

idのみ必須。それ以外は任意。

アイテムの追加

ga('ecommerce:addItem', {
  'id': '1234',                     // トランザクションID
  'name': 'Fluffy Pink Bunnies',    // 商品名
  'sku': 'DD23444',                 // SKU番号、商品ID
  'category': 'Party Toys',         // 商品カテゴリ
  'price': '11.99',                 // 単価
  'quantity': '1'                   // 数量
});

idnameが必須。それ以外は任意。

複数の種類の商品を注文した場合、すべての商品に対して実行する。概念的には

for (すべての商品) {
  ga('ecommerce:addItem', { ... });
}

とすることになる。

PHPであれば

<?php foreach ($item in $items) { ?>
  ga('ecommerce:addItem', {
    'id': '<?php echo($item["id"]) ?>',
    'name': '<?php echo($item["name"]) ?>',
    'sku': '<?php echo($item["sku"]) ?>',
    'category': '<?php echo($item["category"]) ?>',
    'price': '<?php echo($item["price"]) ?>',
    'quantity': '<?php echo($item["quantity"]) ?>'
  });
<?php } ?>

まとめて

<script>
ga('create', 'UA-9999999-1', 'auto');
ga('require', 'ecommerce');
ga('ecommerce:addTransaction', {
  'id': '1234',                     // トランザクションID
  'affiliation': 'Acme Clothing',   // 店舗名やアフィリエイト名
  'revenue': '11.99',               // 購入総額
  'shipping': '5',                  // 送料
  'tax': '1.29'                     // 税金
});
ga('ecommerce:addItem', {
  'id': '1234',                     // トランザクションID
  'name': 'Fluffy Pink Bunnies',    // 商品名
  'sku': 'DD23444',                 // SKU番号、商品ID
  'category': 'Party Toys',         // 商品カテゴリ
  'price': '11.99',                 // 単価
  'quantity': '1'                   // 数量
});
ga('ecommerce:send');
</script>

詳細はGoogle公式のドキュメント参照

https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce

必要な要素の取得方法

Javascriptを使って必要な項目をページ(HTML)内の要素からDOMを使って指定し、取得する。
jQueryを使わずにネイティブのJavascriptで実装する。

element IDが付けられている場合

注文番号や金額の項目にelement IDが付けられている場合はdocument.getElementById('エレメントID')を使えばいい。

element IDがない場合

element IDがない場合もあるが、その場合はdocument.querySelector('CSSパス')を使ってCSSパスで指定することができる。ただしこの場合CSSパスが変わる、HTMLのレイアウトが変わるとデータを読み込めなくなることがあることに留意しておく必要がある。

標準的な実装

設置個所(eコマーストラッキングタグを発火させるURL)の指定

eコマーストラッキングのタグは注文(コンバージョン)完了ページでのみ発火させる。
注文完了ページのURLを
https://example.com/cart/thanks.html
とする場合、

if (window.location.href == 'https://example.com/cart/thanks.html') {
  ga('create', 'UA-9999999-1', 'auto');
  ga('require', 'ecommerce');
    :
  ga('ecommerce:send');
}

完了ページのURLにパラメータなど動的な要素を含む、
URLを完全一致で指定できない場合はパス名の前方一致で

if (/^\/cart\/thanks\.html/.test(window.location.pathname)) {
    :
}

注文(トランザクション)情報

GAに送信する項目は

  • トランザクションID(必須)
  • 店舗名やアフィリエイト名(任意)
  • 購入総額(任意)
  • 送料(任意)
  • 税金(任意)

注文完了ページには何かあった時のための問合せ用の注文番号が記載されている。
それがトランザクションIDになるので、DOMで取得する。

店舗名・アフィリエイト名は固定値になるだろう。

購入総額、送料、税金は、
たとえば注文情報が以下のHTMLで表示されている場合

<table id="order_result">
  <tr>
    <td>ご注文番号</td>
    <td>12345</td>
  </tr>
  <tr>
    <td>消費税</td>
    <td>\962</td>
  </tr>
  <tr>
    <td>送料</td>
    <td>\1,000</td>
  </tr>
  <tr>
    <td>購入金額</td>
    <td>\13,000</td>
  </tr>
</table>

各項目をCSSパスで指定する。

項目名 CSSパス
トランザクションID #order_result > tbody > tr:nth-child(1) > td:nth-child(2)
購入総額 #order_result > tbody > tr:nth-child(4) > td:nth-child(2)
送料 #order_result > tbody > tr:nth-child(3) > td:nth-child(2)
税金 #order_result > tbody > tr:nth-child(2) > td:nth-child(2)

金額は「\」マークやカンマが入ることがあるので、それらを除外して数値としてパースする。

項目名
トランザクションID document.querySelector('#order_result > tbody > tr:nth-child(1) > td:nth-child(2)').innerText
購入総額 document.querySelector('#order_result > tbody > tr:nth-child(4) > td:nth-child(2)').innerText.split(',').join('').trim().match(/\d+/)[0]
送料 document.querySelector('#order_result > tbody > tr:nth-child(3) > td:nth-child(2)').innerText.split(',').join('').trim().match(/\d+/)[0]
税金 document.querySelector('#order_result > tbody > tr:nth-child(2) > td:nth-child(2)').innerText.split(',').join('').trim().match(/\d+/)[0]

string.split(',').join('').trim().match(/^\d+/)[0]で金額の数値のみ取得するところがポイント。

もしIDが指定されている場合、

項目名
トランザクションID document.getElementById('#order_number').innerText
購入総額 document.getElementById('#purchase_amount').innerText.split(',').join('').trim().match(/\d+/)[0]
送料 document.getElementById('#shipping_fee').innerText.split(',').join('').trim().match(/\d+/)[0]
税金 document.getElementById('#tax').innerText.split(',').join('').trim().match(/\d+/)[0]

など楽になる。

個別商品(注文明細)

GAに送信する項目は

  • トランザクションID(必須)
  • 商品名(任意)
  • SKU番号、商品ID(必須)
  • 商品カテゴリ(任意)
  • 単価(任意)
  • 数量(任意)

複数の商品を購入したらその商品数分の明細が発生する。
明細の各行に対してaddItemする必要がある。

たとえば注文情報が以下のHTMLで表示されている場合

<div id="order_number">
  注文番号:<span id="num_text">12345</span>
</div>

<div id="order_detail">
  ご注文内容
  <table>
    <tr>
      <th>商品コード</th>
      <th>商品名</th>
      <th>単価</th>
      <th>数量</th>
    </tr>
    <tr>
      <td>A0001</td>
      <td>元気になる青汁</td>
      <td>\13,000</td>
      <td>1</td>
    </tr>
    <tr>
      <td>B0002</td>
      <td>マルチビタミンAAA</td>
      <td>\3,000</td>
      <td>2</td>
    </tr>
    <tr>
      <td>B0003</td>
      <td>中古のスマートフォンBBB</td>
      <td>\6,000</td>
      <td>1</td>
    </tr>
    <tr>
      <td colspan="3">消費税</td>
      <td>\962</td>
    </tr>
    <tr>
      <td colspan="3">送料</td>
      <td>\1,000</td>
    </tr>
    <tr>
      <td colspan="3">合計</td>
      <td>\962</td>
    </tr>
  </table>
</div>

注文内容テーブルの見出し、消費税、送料、合計は固定で、残りが明細行ごとの繰り返しになる。

CSSパスで指定すると、

注文親情報

項目名 DOM
トランザクションID #num_text
購入総額 #order_detail > table > tbody > tr:nth-child(7) > td:nth-child(2)
送料 #order_detail > table > tbody > tr:nth-child(6) > td:nth-child(2)
税金 #order_detail > table > tbody > tr:nth-child(5) > td:nth-child(2)

明細

項目名 DOM
商品コード(SKU) #order_detail > table > tbody > tr:nth-child(2) > td:nth-child(1)
商品名 #order_detail > table > tbody > tr:nth-child(2) > td:nth-child(2)
単価 #order_detail > table > tbody > tr:nth-child(2) > td:nth-child(3)
点数 #order_detail > table > tbody > tr:nth-child(2) > td:nth-child(4)

ただし明細は

tr:nth-child(2)tr:nth-child(4)

となる。iRows

var iRows = document.querySelectorAll('#order_detail > table > tbody > tr').length;

とすると、明細のループ処理で

行数 項目名
1行目 見出し
2~iRows - 3行目 明細
iRows - 2行目 消費税
iRows - 1行目 送料
iRows行目 合計金額

となる。

まとめ

Googleアナリティクスのトラッキングコードとの前後関係は問わず、任意の位置に設置可能

if (window.location.pathname.indexOf('/cart/thanks.html') > -1) {

  // タイミングがDOM構築完了時なので、Googleアナリティクスのデフォルトのトラッキングコードと無関係の任意の位置に設置可
  document.addEventListener('DOMContentLoaded', function(){

      var iRows = document.querySelectorAll('#order_detail > table > tbody > tr').length;

      ga('require', 'ecommerce');
      ga('ecommerce:addTransaction', {
        'id': document.getElementById('num_text').innerText,
        'revenue': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + iRows + ') > td:nth-child(2)').innerText.split(',').join('').trim().match(/\d+/)[0],
        'shipping': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + (iRows-1) + ') > td:nth-child(2)').innerText.split(',').join('').trim().match(/\d+/)[0],
        'tax': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + (iRows-2) + ') > td:nth-child(2)').innerText.split(',').join('').trim().match(/\d+/)[0]
      });

      for (var i = 2, imax = iRows - 3; i <= imax; i++) {
        ga('ecommerce:addItem', {
          'id': document.getElementById('num_text').innerText,
          'sku': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + i + ') > td:nth-child(1)').innerText,
          'name': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + i + ') > td:nth-child(2)').innerText,
          'price': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + i + ') > td:nth-child(3)').innerText.split(',').join('').trim().match(/\d+/)[0],
          'quantity': document.querySelector('#order_detail > table > tbody > tr:nth-child(' + i + ') > td:nth-child(4)').innerText.split(',').join('').trim().match(/\d+/)[0]
        });
      }

      ga('ecommerce:send');

  }, false);

}

Googleアナリティクス関連Tips

イベント計測

ページビュー計測

eコマース計測

Google の記事一覧