PR

Cocoonで測れない広告クリック率、AIに頼んだら10分で測れました

Cocoon×AIカスタマイズ

先に結論です。エージェントAI(Claude Code)に「アフィリエイトリンクのクリック率を測りたい」と頼んだら、10分後、WordPressの管理画面に「広告計測」というメニューが増えていました。コードは1行も書いていません。

AIが作ったCocoonの広告クリック計測画面。広告タグ×記事の単位で表示回数・クリック・CTRが並ぶ
できあがった管理画面。どの記事の、どの広告かまで見えます

広告のクリック計測は、Cocoonにない機能です。今回はデザインの模様替えではなく、テーマに「ない機能」そのものをAIに作ってもらいました。頼んだ言葉も、できあがった仕組みも、コピペで使えるコードも、全部この記事に載せます。

MARU
MARU

いままではA8の管理画面とにらめっこして、どの記事のリンクが押されたのか謎解きしてました

Cocoonでは、広告のクリック数が見えません

Cocoonには「アフィリエイトタグ」という便利な仕組みがあります。広告コードを1箇所に登録して、記事には エックスサーバーを公式サイトでチェック と書くだけ。差し替えも一括でできる、よくできた機能です。

ただ、この画面に数字は何もありません。何回表示されて、何回押されたのか。それはA8などASP側のレポートを見に行くことになります。そしてASPのレポートで分かるのは合計だけ。「どの記事の、どのリンクが押されたのか」は、どこにも出てきません

Cocoonのアフィリエイトタグ管理画面。ショートコードと説明だけで計測の数字はない
アフィリエイトタグの管理画面。ここに数字の欄はありません

この機能、望んでいる人はずっといました。Cocoon公式フォーラムには2018年4月、「アフィリエイトタグのクリック数測定」という要望が投稿されています。「管理画面で管理出来れば非常に便利ですので」という、いま読んでもまったく同感のお願いです(該当スレッド)。

開発者のわいひらさんの返信は「クリック集計も視野に入れた設計にはなっています」。ただ、簡単な機能ではなく開発コストが重い、という内容でした。それから8年、この機能は実装されていません。無料テーマの限られた開発リソースを何に使うかを考えれば、責められる話ではないと思います。

ちなみに有料テーマのSWELLには、広告タグごとの表示回数・クリック率の計測機能が最初から付いています。「計測したいならSWELLへ」という乗り換え記事が多いのも、この差があるからです。

AIへの頼み方は、今回も一言でした

頼んだ言葉は、これだけです。

Cocoonでアフィリエイトリンクのクリック率を測りたい。SWELLみたいにWordPressの管理画面で見られるようにして

仕様書もコードも渡していません。この実演をやっているのは、ファイルの編集や動作確認まで代行してくれるエージェントAIです(このブログの主役。詳しくはこちらの記事)。ChatGPTなど対話型AIでのやり方は、記事の最後にまとめました。

10分後、管理画面に「広告計測」が増えていました

頼んだのが朝の7時54分。管理画面に計測の数字が表示されたのが8時4分。ちょうど10分でした。その間にAIがやったことは、こうです。

  1. Cocoonの仕組みを調査(公式の公開ソースを読みにいく)
  2. 計測プログラムを書く(表示とクリックを数えて保存する仕組み)
  3. 子テーマのfunctions.phpに追記して保存
  4. 実際にページを開いてクリックし、数字が増えることを確認

できあがった「広告計測」でできることは3つ。広告ごとの表示回数・クリック数・CTR(クリック率)が見えること。自分がログインして操作した分は数えないこと。ボタン1つでデータをリセットできること。表示回数は「画面に半分以上見えたら1回」と数える方式で、ページの下の方にあって読まれなかった広告はカウントされません。地味にえらいポイントです。

AIが見つけた、8年前の伏線

作業中のAIの報告に、面白い発見がありました。Cocoonが出力する広告リンクのHTMLを見ると、こうなっています。

<a rel="nofollow" data-atag-id="2" data-post-id="66" href="https://px.a8.net/...">

data-atag-id(どの広告タグか)と data-post-id(どの記事に貼られたか)。計測に必要な情報が、最初からリンクに埋め込まれていたんです。フォーラムでわいひらさんが書いていた「クリック集計も視野に入れた設計」は、言葉どおり本当にコードの中に用意されていました。

つまり今回AIが作ったのは、8年前から敷かれていたレールの上を走る列車のようなものです。Cocoonの設計に、AIが計測部分を接続しただけ。だからHTMLもデザインも一切いじらずに済んでいます。

「どの記事の、どのリンクか」も、追加の一言で付きました

最初の版の集計は広告ごとの合計だけでした。そこで「どのページの、どのリンクが押されたかも分かるの?」と聞いてみたところ、さきほどのdata-post-idを使う拡張を提案してきて、5分後には記事別の内訳が表示されるようになりました

MARU
MARU

「同じ広告を3記事に貼ってるけど、押されてるのはどれ?」が一覧で分かるようになりました

冒頭の画面がその状態です。広告タグごとの合計の下に、記事タイトル付きの内訳がぶら下がる2階建て。機能が足りなければ、その場で頼んで足せる。既製品との一番の違いはここだと感じます。

今回は作っていないことも、書いておきます

同じ記事の中に同じ広告タグを2箇所貼った場合、どちらが押されたかまでは今回は作り込んでいません(ちなみに既製品のSWELLの広告タグ計測も、ここはタグ単位です)。頼めば「記事上のボタン」「記事下のボタン」を区別する拡張も作れます。まずは使ってみて、必要になったら一言足す。あとから足せるのが自作の良さです。

ロボットのアクセスを厳密に見分ける仕組みも入れていません。ここは作り込むほど大掛かりになるわりに、得られるものが少ない場所。数字は広告の置き場所を考えるための目安と割り切るのが、手間と釣り合うと思います。

それと、自作した機能の面倒を見るのは自分です。テーマの大型更新で動かなくなったら、直すのも自分(とAI)。既製の有料テーマは、この面倒ごとをお金で開発者に預ける仕組みなんだと、作ってみてよく分かりました。

判定: 🟡 工夫が要った

カスタマイズ実例の恒例、3段階判定です。

  • 🟢 AIで一発 = 一言頼んで、修正なしで完成
  • 🟡 工夫が要った = 頼み直しやプロンプトの工夫が必要だった
  • 🔴 別の道が早い = 手動やスキンのほうが早い

ボタン記事のときに「大物になるほど🟡や🔴が出てくるはず。出たら出たで、そのまま書きます」と書きました。今回がその第1号です。最初の版はスマホで広告ボタンの表示が崩れて、画面の様子を言葉で伝えて作り直してもらいました。だから🟡。それでも、コードを1行も書かずにテーマの機能が1つ増えた事実は変わりません。

🔴側の線引きも書いておきます。数字を見る場所がASPのレポートで足りているなら、作る必要はありません。この道が活きるのは、「どの記事のどの広告が働いているか」を見て、貼り場所や文言を変えていきたい人です。

ChatGPTなど、対話型AIでやる場合

最後に、チャット画面で使う対話型AIでのやり方です。この記事の実演はファイルの書き込みや動作確認までAIがやりましたが、対話型AIでも作れるものは同じです。違いは、出てきたコードを自分で子テーマのfunctions.phpに貼ること。作業は1回増えますが、やることは貼り付けだけです。

頼み方の文例です。そのままコピペで使えます。

WordPressテーマCocoonで、アフィリエイトタグ [affi id=N] の表示回数とクリック数を計測して、管理画面で見られるようにするPHPコードを書いてください。子テーマのfunctions.phpに貼って使います。Cocoonの広告リンクには data-atag-id と data-post-id という属性が付いているので、それを使ってください。

貼るときの注意は3つです。必ず子テーマのfunctions.phpに貼ること(親テーマはアップデートで消えます)。貼る前にCocoon設定のバックアップ機能でバックアップを取ること。保存ボタンを押したら「ファイルの編集に成功しました」の表示まで確認すること。WordPressはコードに致命的な間違いがあると自動で元に戻してくれるので、画面が真っ白のまま戻れなくなる事故は起きにくくなっています。

参考までに、今回AIが書いたコードの全文も置いておきます。これをそのまま貼っても動きますし、対話型AIに「これを下敷きに」と渡すのも近道です。

<?php //子テーマ用関数
if ( !defined( 'ABSPATH' ) ) exit;

//子テーマ用のビジュアルエディタースタイルを適用
add_editor_style();

//以下に子テーマ用の関数を書く

// ===== 広告クリック計測(AIAD)ここから =====
// [affi id=N] の表示回数・クリック数を、広告タグ×記事の単位で計測して管理画面「広告計測」で見る
// v3: Cocoonがリンクに付けている data-atag-id(広告タグID)と data-post-id(貼られた記事ID)を
//     そのまま計測に使う。HTMLには一切手を加えない

// 1) 集計の保存と加算(wp_options・キーは「タグID_記事ID」)
function aiad_get_stats() {
  $s = get_option('aiad_stats', array());
  return is_array($s) ? $s : array();
}
function aiad_add_hit($tag, $pid, $type) {
  $s = aiad_get_stats();
  $key = intval($tag) . '_' . intval($pid);
  if (!isset($s[$key]) || !is_array($s[$key])) $s[$key] = array('imp' => 0, 'click' => 0);
  $s[$key][$type]++;
  update_option('aiad_stats', $s, false);
}

// 2) 計測の受け口(REST API)
add_action('rest_api_init', function () {
  register_rest_route('aiad/v1', '/hit', array(
    'methods' => 'POST',
    'permission_callback' => '__return_true',
    'callback' => function ($req) {
      $tag = intval($req->get_param('id'));
      $pid = intval($req->get_param('pid'));
      $type = $req->get_param('type');
      if ($tag < 1 || $tag > 99999 || $pid < 0 || !in_array($type, array('imp', 'click'), true)) {
        return new WP_Error('aiad_bad_request', 'invalid', array('status' => 400));
      }
      aiad_add_hit($tag, $pid, $type);
      return array('ok' => true);
    },
  ));
});

// 3) フロント計測JS(ログイン中=自分は出力しない→カウントされない)
add_action('wp_footer', function () {
  if (is_user_logged_in()) return;
  ?>
  <script id="aiad-tracker">
  (function () {
    var els = document.querySelectorAll('[data-atag-id]');
    if (!els.length) return;
    var url = '<?php echo esc_url_raw(rest_url('aiad/v1/hit')); ?>';
    var seen = {};
    function send(id, pid, type) {
      try {
        var fd = new FormData();
        fd.append('id', id);
        fd.append('pid', pid);
        fd.append('type', type);
        navigator.sendBeacon(url, fd);
      } catch (e) {}
    }
    function idsOf(el) {
      return {
        tag: el.getAttribute('data-atag-id'),
        pid: el.getAttribute('data-post-id') || '0'
      };
    }
    if ('IntersectionObserver' in window) {
      var io = new IntersectionObserver(function (entries) {
        entries.forEach(function (en) {
          if (en.isIntersecting) {
            var ids = idsOf(en.target);
            var k = ids.tag + '_' + ids.pid;
            if (!seen[k]) {
              seen[k] = 1;
              send(ids.tag, ids.pid, 'imp');
              io.unobserve(en.target);
            }
          }
        });
      }, { threshold: 0.5 });
      els.forEach(function (el) { io.observe(el); });
    }
    document.addEventListener('click', function (ev) {
      var t = ev.target && ev.target.closest ? ev.target.closest('[data-atag-id]') : null;
      if (t) {
        var ids = idsOf(t);
        send(ids.tag, ids.pid, 'click');
      }
    }, true);
  })();
  </script>
  <?php
});

// 4) 管理画面「広告計測」(タグ別合計+記事別内訳+リセット)
add_action('admin_menu', function () {
  add_menu_page('広告計測', '広告計測', 'manage_options', 'aiad-stats', 'aiad_render_admin', 'dashicons-chart-bar', 58);
});
function aiad_ctr($imp, $clk) {
  return $imp ? round($clk / $imp * 100, 2) . '%' : '—';
}
function aiad_render_admin() {
  if (isset($_POST['aiad_reset']) && check_admin_referer('aiad_reset_action')) {
    delete_option('aiad_stats');
    echo '<div class="updated"><p>計測データをリセットしました。</p></div>';
  }
  $s = aiad_get_stats();
  // 「タグID_記事ID」形式のキーだけ集計(旧形式のテスト値は無視)
  $groups = array();
  foreach ($s as $key => $d) {
    if (strpos($key, '_') === false || !is_array($d)) continue;
    list($tid, $pid) = explode('_', $key, 2);
    $groups[intval($tid)][intval($pid)] = array('imp' => intval($d['imp']), 'click' => intval($d['click']));
  }
  ksort($groups);
  echo '<div class="wrap"><h1>広告クリック計測</h1>';
  echo '<p>[affi id=N] で貼った広告の表示回数とクリックを、広告タグ×記事の単位で自動集計します(ログイン中の操作は数えません)。</p>';
  echo '<table class="widefat striped" style="max-width:760px">';
  echo '<thead><tr><th>広告タグ/記事</th><th>表示回数</th><th>クリック</th><th>CTR</th></tr></thead><tbody>';
  if (!$groups) {
    echo '<tr><td colspan="4">まだ計測データがありません。</td></tr>';
  }
  foreach ($groups as $tid => $rows) {
    $name = 'タグID ' . $tid;
    if (function_exists('get_affiliate_tag') && ($r = get_affiliate_tag($tid))) {
      if (!empty($r->title)) $name = $r->title;
      elseif (!empty($r->name)) $name = $r->name;
    }
    $timp = 0; $tclk = 0;
    foreach ($rows as $d) { $timp += $d['imp']; $tclk += $d['click']; }
    echo '<tr style="background:#f0f6fc;font-weight:bold"><td>' . esc_html($name) . '(合計)</td><td>' . $timp . '</td><td>' . $tclk . '</td><td>' . esc_html(aiad_ctr($timp, $tclk)) . '</td></tr>';
    ksort($rows);
    foreach ($rows as $pid => $d) {
      if ($pid && ($title = get_the_title($pid))) {
        $label = '<a href="' . esc_url(get_permalink($pid)) . '" target="_blank" rel="noopener">' . esc_html($title) . '</a>';
      } elseif ($pid) {
        $label = '投稿ID ' . $pid;
      } else {
        $label = '(記事不明)';
      }
      echo '<tr><td style="padding-left:2em">' . $label . '</td><td>' . intval($d['imp']) . '</td><td>' . intval($d['click']) . '</td><td>' . esc_html(aiad_ctr($d['imp'], $d['click'])) . '</td></tr>';
    }
  }
  echo '</tbody></table>';
  echo '<form method="post" style="margin-top:12px" onsubmit="return confirm(\'計測データを全部消します。よろしいですか?\');">';
  wp_nonce_field('aiad_reset_action');
  submit_button('計測データをリセット', 'delete', 'aiad_reset', false);
  echo '</form></div>';
}

// 5) ABテスト [aiad_ab a=X b=Y](AとBの広告タグをランダム出し分け・集計は上の仕組みに相乗り)
add_shortcode('aiad_ab', function ($atts) {
  $a = isset($atts['a']) ? intval($atts['a']) : 0;
  $b = isset($atts['b']) ? intval($atts['b']) : 0;
  if (!$a || !$b) return '';
  $pick = mt_rand(0, 1) ? $a : $b;
  return do_shortcode('[affi id=' . $pick . ']');
});
// ===== 広告クリック計測(AIAD)ここまで =====

まとめ: 「テーマにない機能」は、諦める理由にならなくなった

  • 頼んだのは一言。「クリック率を測りたい。管理画面で見たい」
  • 10分で管理画面に「広告計測」が増えた。コードは書いていない
  • 「どの記事のどのリンクか」は、追加の一言で5分

8年前の要望スレッドを読み返すと、欲しかったものはシンプルで、「管理画面で見たい」それだけなんですよね。テーマの機能一覧に載っていないことは、もう諦める理由になりません。欲しい機能を言葉にできれば、それが仕様書になります。

デザイン側の実例はボタン配色トップページに。ブログをこれから始める場合は始め方の全体マップからどうぞ。

コメント

タイトルとURLをコピーしました