本日0件のお問い合わせがありました。

✉️FIELDにお問い合わせ

マーケティングアプリケーション開発

ユーザ行動分析の答え

こんにちは山本です。

どのデバイスでどこに住む、どこのページから流入したユーザがどこを回遊し、
どこをクリックし、どの順番にどのようにフォームを入力しCVするのか
または離脱したのか

これを定量的に知る術をみなさんあまり知りません。
上場企業でもメガベンチャーでもベンチャーでも
意外とそのような分析体制がないことを知っています。
いつもここから整理するからです。

以下、私がいつもやることを書きますが、
googleのエンジニアやマーケターに聞いてもこれが答えだということだったので
おそらくそれなりに筋は良いかと思います。

どのように行動分析をできるようにするのか

まず、サイトのすべてのページにページID、
パーツにpart番号を振っていきます。

こういう感じで、このページを開いたらページ種別がTOPで、
そのこの部分をクリックしたらpart_idが34番などと
全てにdata属性を振っておき、それをGTMの変数を作りbigqueryに送信します。

コードで見るとこんな感じになります。

そして全ページに
リードステータス
ゲストID
流入時のutmパラメーター
DBのユーザID
ページ種別
デバイス
パートID

などを
定義します。

上記は一部です。
そしてこれをGTMの変数として定義し、bigqueryに飛ばします。

bigqueryでは

このようなsqlで特定の期間の行動履歴を出すことができます。

そして取得した行動履歴を
吐き出したものがこちらです

これができたら、
例えば一例でgasスクリプトを記載しますが

/**
 * "results-20250603-151144" シートの C 列に '"part":"98"' または '"part":"99.1"' を含む行を抽出し、
 * "cta_click_user_list" シートにコピーします。
 */
function extractRowsByPartInColumnC() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var srcSheetName = 'results-2xx3-5xx4';
  var dstSheetName = 'cta_click_user_list';

  // ソースシート取得
  var srcSheet = ss.getSheetByName(srcSheetName);
  if (!srcSheet) {
    SpreadsheetApp.getUi().alert('シート "' + srcSheetName + '" が見つかりません。');
    return;
  }

  // 抽出先シートを取得 or 新規作成
  var dstSheet = ss.getSheetByName(dstSheetName);
  if (dstSheet) {
    dstSheet.clearContents();
  } else {
    dstSheet = ss.insertSheet(dstSheetName);
  }

  // 全データを取得
  var values = srcSheet.getDataRange().getValues();

  var output = [];
  for (var i = 0; i < values.length; i++) {
    var row = values[i];
    var cCell = row[2]; // C列はインデックス 2
    if (cCell == null) continue;

    var text = cCell.toString();
    if (text.indexOf('"part":"98"') !== -1 || text.indexOf('"part":"99.1"') !== -1) {
      output.push(row);
    }
  }

  if (output.length === 0) {
    SpreadsheetApp.getUi().alert('該当する行が見つかりませんでした。');
    return;
  }

  // 抽出結果を A1 から書き込む
  dstSheet.getRange(1, 1, output.length, output[0].length).setValues(output);
  SpreadsheetApp.getUi().alert('抽出完了:' + output.length + ' 行を "' + dstSheetName + '" シートにコピーしました。');
}



/**
 * "cta_click_user_list" シートの C 列から gid を抽出し、
 * 同じ行の I 列に書き込みます。
 */
function get_gid_from_cta_click_user() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetName = 'cta_click_user_list';
  var sheet = ss.getSheetByName(sheetName);
  if (!sheet) {
    SpreadsheetApp.getUi().alert('シート "' + sheetName + '" が見つかりません。');
    return;
  }

  // シートの最終行を取得
  var lastRow = sheet.getLastRow();
  if (lastRow < 1) return;

  // C列のデータを取得(1 行目から最終行まで)
  var cRange = sheet.getRange(1, 3, lastRow, 1);
  var cValues = cRange.getValues();

  // I列に書き込むための2次元配列を準備
  var output = [];

  for (var i = 0; i < cValues.length; i++) {
    var cellText = cValues[i][0];
    var gid = '';

    if (cellText && typeof cellText === 'string') {
      // '{"uid":0,"gid":"' で分割
      var parts = cellText.split('{"uid":0,"gid":"');
      if (parts.length > 1) {
        // 2番目の要素を先頭の '"' で再分割して gid を抽出
        var remainder = parts[1];
        var secondParts = remainder.split('"');
        if (secondParts.length > 0) {
          gid = secondParts[0];
        }
      }
    }

    output.push([gid]);
  }

  // I列に一括で書き込む
  sheet.getRange(1, 9, output.length, 1).setValues(output);
}



/**
 * "cta_click_user_list" シートの I 列の値を重複を除いて
 * J 列に書き出します。
 */
function get_nayose_merge_cta_click_usa() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheetName = 'cta_click_user_list';
  var sheet = ss.getSheetByName(sheetName);
  if (!sheet) {
    SpreadsheetApp.getUi().alert('シート "' + sheetName + '" が見つかりません。');
    return;
  }

  // I列の最終行を取得
  var lastRow = sheet.getLastRow();
  if (lastRow < 1) return;

  // I列(9列目)のデータを1行目から最終行まで取得
  var iValues = sheet.getRange(1, 9, lastRow, 1).getValues();

  // 重複を排除するセットを作成
  var uniqueSet = new Set();
  for (var i = 0; i < iValues.length; i++) {
    var val = iValues[i][0].toString().trim();
    if (val !== '') {
      uniqueSet.add(val);
    }
  }

  // セットを配列に変換し、2次元配列に整形
  var uniqueArray = Array.from(uniqueSet).map(function(item) {
    return [item];
  });

  // J列(10列目)に書き込み
  if (uniqueArray.length > 0) {
    sheet.getRange(1, 10, uniqueArray.length, 1).setValues(uniqueArray);
  }
}

/**
 * "cta_click_user_list" シートの J 列の gid を一つずつ取得し、
 * "results-20250603-151144" シートの C 列を検索して
 * 当該 gid に対して '"cv":"1"' が一度も出現しない場合、
 * cta_click_user_list シートの同じ行の K 列に gid を書き込みます。
 */
function get_not_cv_user_from_cta_list() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var ctaSheetName = 'cta_click_user_list';
  var resultsSheetName = 'results-20250603-151144';

  var ctaSheet = ss.getSheetByName(ctaSheetName);
  if (!ctaSheet) {
    SpreadsheetApp.getUi().alert('シート "' + ctaSheetName + '" が見つかりません。');
    return;
  }
  var resultsSheet = ss.getSheetByName(resultsSheetName);
  if (!resultsSheet) {
    SpreadsheetApp.getUi().alert('シート "' + resultsSheetName + '" が見つかりません。');
    return;
  }

  // cta シートの最終行を取得
  var ctaLastRow = ctaSheet.getLastRow();
  if (ctaLastRow < 1) return;

  // J列(10列目)のデータを取得(1行目~最終行まで)
  var jValues = ctaSheet.getRange(1, 10, ctaLastRow, 1).getValues();

  // results シートの最終行を取得
  var resultsLastRow = resultsSheet.getLastRow();
  var resultsCValues = [];
  if (resultsLastRow >= 1) {
    // C列(3列目)のデータを取得(1行目~最終行まで)
    resultsCValues = resultsSheet.getRange(1, 3, resultsLastRow, 1).getValues();
  }

  // K列(11列目)に出力するための配列を用意
  var output = [];
  for (var i = 0; i < ctaLastRow; i++) {
    var gid = (jValues[i][0] || '').toString().trim();
    if (gid === '') {
      // J列が空なら K列も空
      output.push(['']);
      continue;
    }

    // results の C列を検索して、同一 gid かつ '"cv":"1"' を含む行があるかチェック
    var hasCvOne = false;
    var searchPattern = '"gid":"' + gid + '"';
    for (var r = 0; r < resultsCValues.length; r++) {
      var text = (resultsCValues[r][0] || '').toString();
      if (text.indexOf(searchPattern) !== -1 && text.indexOf('"cv":"1"') !== -1) {
        hasCvOne = true;
        break;
      }
    }

    if (hasCvOne) {
      // '"cv":"1"' が見つかった → K列は空
      output.push(['']);
    } else {
      // '"cv":"1"' が一度も見つからなかった → K列に gid を書き込む
      output.push([gid]);
    }
  }

  // K列(11列目)に一括で書き込む
  ctaSheet.getRange(1, 11, output.length, 1).setValues(output);
}

つまり行動履歴からさまざまなパターンのユーザを抽出します。

CVしたユーザだけでなく、
見積もりボタン押したのに、CVしなかったユーザ
などの一連のこうどうの履歴を出していきます。

{“uid”:0,”gid”:”NslfRtF3″,”pg”:”LIST”,”pre”:”LIST”,”l_id”:5,”cv”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”utm”:””,”LD”:”7″,”l_id”:5,”device”:”PC”}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”LIST”,”l_id”:5,”part”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”LIST”,”pre”:”LIST”,”l_id”:5,”cv”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”utm”:””,”LD”:”7″,”l_id”:5,”device”:”PC”}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”LIST”,”l_id”:5,”part”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”utm”:””,”LD”:”7″,”l_id”:5,”device”:”PC”}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”LIST”,”l_id”:5,”part”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”LIST”,”pre”:”CAR”,”l_id”:5,”cv”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”CAR”,”pre”:”DET”,”l_id”:25,”cv”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”utm”:””,”LD”:”15″,”l_id”:25,”device”:”PC”}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”CAR”,”l_id”:25,”part”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”act”:”1″,”cv”:”0″,”part”:”98″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”act”:”1″,”cv”:”0″,”part”:”96″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”act”:”1″,”cv”:”0″,”part”:”93″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”act”:”1″,”cv”:”0″,”part”:”93″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”act”:”1″,”cv”:”0″,”part”:”94″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”act”:”1″,”cv”:”0″,”part”:”94″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”pre”:”DET”,”l_id”:20,”cv”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”utm”:””,”LD”:”10″,”l_id”:20,”device”:”PC”,”p_id”:”8545″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”part”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”pre”:”DET”,”l_id”:20,”cv”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”utm”:””,”LD”:”10″,”l_id”:20,”device”:”PC”,”p_id”:”8571″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:20,”part”:”0″}
{“uid”:0,”gid”:”NslfRtF3″,”pg”:”DET”,”l_id”:”15″,”act”:”1″,”cv”:”0″,”part”:”2006″}

そして、定義の際にpartに説明をつけた内容とGPTにプロンプトをなげると

【コンバージョン未達ユーザーAの動き】

gid = “NslfRtF3”
uid = 0(未ログイン / CVに至らず)


◾️概要

PC利用、東京都(三鷹市)およびドイツ(フランクフルト)からのアクセスが確認されたユーザー。
自然検索(utm空)から、商品ID: 8545・8571・4967・8489 など複数の商品詳細ページ(pg: DET)にたびたび流入。

サイズ(part 93)や注文数(96)、価格表(94)、見積もり発行(98)といった購入直前アクションまでは実行しているが、ログインもせず、最終的にCVには至らなかった

テンプレートのダウンロード(part 100)や最近チェックした商品からの再訪(112)もあり、購買意欲は非常に高いユーザーと推察される。


◾️ユーザー行動自然言語化詳細(全アクセス履歴)


🕓 2025-05-25(東京都 三鷹市)

  • 10:17:17|自然検索から、**商品詳細ページ(商品ID: 8545)**へ初回流入。
  • 10:17:22|商品詳細ページ(p_id: 8545)を再読込(part: 0)。
  • 10:17:30〜10:17:38|FV画像(part: 90)を複数回クリック。商品画像を確認中。
  • 10:35:03|おすすめ商品からの導線(part: 111)をクリックして別商品へ。
  • 10:35:10商品ID: 4967 の商品詳細ページに遷移。
  • 10:35:37|最近チェックした商品からの導線(part: 112)をクリック → 再び商品詳細へ戻る。
  • 10:35:43|商品詳細ページ(p_id: 8545)に再訪。FV画像クリック継続(part: 90)。
  • 15:54:47|テンプレートをダウンロード(part: 100)→ この時点で強い購入検討が伺える。

✈️ 2025-05-28(ドイツ Hessen州 Frankfurt)

  • 13:02:09|再びテンプレートをダウンロード(part: 100)→ 商品ID: 8545 の詳細ページ。
  • 14:14:15商品ID: 8571 の詳細ページを読み込み。
  • 14:14:26商品ID: 8545 に戻る。FV画像再チェック(part: 90)。
  • 14:14:41|サイズ(part: 93)、価格表(94)、注文数(96)などをクリック。
  • 14:15:11|見積もり発行検討(part: 98)。
  • 14:15:17|カートページに遷移するも、そのまま離脱(CVなし)。

🕓 2025-05-31(東京都 三鷹市)

  • 22:21:00〜22:21:26|再度、商品ID: 8545 の詳細ページにアクセス。
    FV画像(part: 90)を繰り返しクリックするが、新たな進展は見られず。

📍 位置情報まとめ

日時ロケーション行動内容商品ID(p_id)
5/25 午前三鷹市初回流入〜導線クリック8545 → 4967 → 8545
5/25 午後三鷹市テンプレDL(part: 100)8545
5/28 午後Frankfurtサイズ・価格・見積り・カートへ遷移8571 → 8545
5/31 夜三鷹市FV画像クリックのみ8545

◾️まとめと示唆

このユーザーは、

  • 商品ID: 8545 を中心に、複数の商品を国内外から繰り返し確認
  • テンプレDL、見積もり検討、カート画面到達までしており、購買意志は非常に高い
  • ただし、**ログインせず(uid = 0)**情報入力にも進んでいないため、購入に至らず終了。

📌 施策提案:

  • ログイン誘導や「お気に入り保存」導線の強化
  • 見積り or カート放棄ユーザーへのリマインド通知
  • ロケーションに応じた通貨表示/配送条件表示(国際アクセス対策)

このような行動が丸わかりになる記述を取得できます。

これまではbigqueryの値をredshiftに送って分析などをやっていましたが
結局「費用はなんでも抑えたい/無料の範囲でやりたい」という人が多いのではないかと思います。
そのため、日にちを限定して上記のような方法でbigqueryから定義されたjsonを取得し
定義されたサイトのパートとストーリーをGPTのプロンプトにすれば
だいぶ細かくストーリー性をもった分析ができるようになります。

ちなみにサムネイルはバンコク法人の梶村です。

Yamamoto Yuya

プロフェッショナルとしての高いスキルと知識を持ち、誠実さと責任感を大切にする。常に向上心を持ち、新たな挑戦にも積極的に取り組む努力家。