はてなブログとGA4とデータ活用

こんにちは。はてなブログのサブディレクターをやっている id:AirReader と申します。
さて、2022年3月16日にGoogleよりユニバーサル アナリティクスの終了予定とGoogle アナリティクス 4(以下、GA4)への移行のアナウンスが行われました。GoogleがGA4を推しているのは知っていましたが、これほど早い終了は予想していませんでしたのでとても驚いたのを覚えています。
はてなブログでは、他のブログサービスと比較しても早い段階でブログのGA4に対応いたしました。また、単に対応するだけではなく、はてなブログの解析に最適化したデータを送信する付加価値もつけておりますので、ぜひご利用いただければと思います。
staff.hatenablog.com

はてなブログの開発においては、終了の案内以前からGA4を導入して利用しておりました。また、GA4とは別にはてなブログの利用データはBigQueryに保存しており、両方のデータを合わせて活用しています。そこで、この機会にはてなブログを取り巻くデータとその活用についてまとめておきたいと思います。

はてなブログとデータ活用の歩み

はてなブログとBigQuery

はてなブログのデータをBigQueryで取り扱えるようにしたのが2020年末です。この頃、テックリードの id:polamjag が「データが重要」と繰り返し唱えており、データへの機運が醸成されつつありました。そんな中、ブログで持ち上がった問題に対して、これを好機と捉え、ブログのデータ基盤を作って解決する道筋が立てられ、バリバリと実装が進んでいきました。ハード面だけではなく、同テックリードの提案でチーム内で「DMM.comを支えるデータ駆動戦略」の読書会が実施され、データ活用への機運はチームメンバーへも広がっていきました。
また、ブログ単発の施策にとどまらせず、はてなのコンテンツプラットフォーム全体の体験を改善するために、チーム横断で足並みを揃えてデータ基盤を整える方針も立てられました。

こういった流れの結果、はてなブログだけではなく、はてなスターやはてなブックマークなどのデータも取り扱えるようになりました*1。はてなブログというサービスにとって、はてなスターやはてなブックマークなど著者へのフィードバック機能は、重要な要素のひとつですので、ユーザーに提供するサービスの改善を考えていく上で大きな意義があります。
加えて、BigQueryによる分析が可能になることで「数百万件残っていたHTTPのはてなブログを4年越しにすべてHTTPS化させた話 - Hatena Developer Blog」の「移行するブログ数をBigQueryで把握」のセクションで紹介したような計画にも利用できるようになりました。
GA4は2021年末に導入し、BigQueryにデータがエクスポートされるよう設定しました。これで、はてなブログのほとんどのデータがBigQuery上で取り扱えるようになりました。


もちろん、データがあるだけでは意味はなく、利用できるメンバーがいなければ意味はありません。そういう観点でみると、BigQueryはメンバーの拡大を強力に後押ししてくれました。私はエンジニア出身ではないため、ほぼSQLは書いたことがありませんでしたが、

  • 担当しているサービスのデータであり、分析したい仮説がたくさんあること*2
  • 大規模なデータもマジカルな速度で結果が得られることによってトライアンドエラーの回転速度をあげられること
  • 本番のDBと切り離されていることが明確で、分析用クエリ起因のサービスへの影響を考えなくていいこと
  • クエリで発生するコストが明瞭なこと(クエリでスキャンされたデータ量に対して料金が発生し、BigQuery内のエディタにクエリを書くとデータ量が表示されるため)

などの特徴に支えられて、急速にSQLが扱えるようになったと感じています。特に自分自身が利用しているサービスのデータを、自分の仮説でもってディメンションを切り出し、サービス改善に活かせることほど楽しいことはないな、と思っています。

データの遊び場をつくる

このように分析を進めていくにつれて、この大規模なデータを活用することで、より深くデータが持つ意味を知ったり、これまで以上に高い精度でサービスやユーザーを理解できるようになるのでは?という思いが強くなってきました。
データにどういう意味があるのか、あるいはサービス・ユーザーを理解するための新たなディメンションはないかという視点は、未知のものを既知にしてく視点です。それには現場での遊びも重要だと考えています。そんな考えから、Google データポータルに「HatenaBlog Data Playground」というダッシュボードを作成し、皆で思い思いの仮説を立て良いブログを抽出する活動を開始しました。

やってみると、実際にこれまで意識してなかったいいデータが得られたり、そもそもあまりデータポータルに馴染みがなかったエンジニアが使うきっかけになったりと、色々良い効果があったと感じています。

データポータルと組み合わせてプロトタイピングを実施する

新機能を提供する前に、それに価値があるのかを評価するために、プロトタイピングという選択肢があるかと思います。これにはBigQueryとデータポータルを活用することが私の常套手段となっています。データポータルは基本的なUIパーツが提供されているので、データさえBigQueryにあれば素早くプロトタイプを提供できるためです。
例えば、先日リリースした、記事のレコメンド機能「『おとなり日記』機能」は、データポータルにてプロトタイプを作成しました。このプロトタイプにより素早く機能の評価を行うことができました。

個人的に特にお気に入りなのは、レコメンドの条件のパラメータをUIとして提供し、企画者が自分自身で調整することも可能にした点です。
staff.hatenablog.com

続いて、GA4の導入について紹介します。

GA4の導入

GA4にBigQueryエクスポート機能が備わり、一気に分析の幅が広まりました。また、ユニバーサルアナリティクスから大きく変わりすべてのデータが「イベント」という単位で記録されるようになったことで、データが取り扱いやすくなったと感じています。
ユニバーサル アナリティクスからGA4への移行のメリットは、はてなの運営だけではなく、ユーザー向けの還元にも繋がりました。ユニバーサル アナリティクスのカスタムディメンションの機能ではユーザーのカスタムディメンション設定とはてなが送信するデータがバッティングする可能性があることを鑑みると統一的に提供するのは難しく、またユニバーサル アナリティクスのイベント機能ではクリックイベントや読了イベントなどを綺麗に渡すのが難しかったためです。GA4では、大きな問題を引き起こすことなく実装でき、ユーザーに価値を提供できたのは良かったと思っています。

カスタムディメンションをこちらで指定することで、Googleデータポータルではてなブログ用のダッシュボードを提供できるのでは?、という目論見もあったのですが、今の所データポータルのレポートコピー機能がそれに適さず断念しています。

はてなブログでは計測にGTMを利用しています。特定のクラスが含まれているエレメントのクリックをトリガーとしてGA4にイベントを送信するなど、便利なユーティリティが提供されていることが主な理由です。はてなブログで導入した具体的な実装について解説します。

クリックイベント, 要素の表示イベントの計測

クリック計測は .track-click-by-gtm を含むエレメントをクリックのトリガーの条件にして、そのエレメントに含めた data-gtm-track-json 属性のjsonの値から、GA4に送信するデータを抽出しています。データの内訳は以下のように標準化しています。

{
  area: string //クリックされた領域を示す。1ページに複数のユニットが含まれている場合などで役に立ちます
  component: string //リンクユニットにはタイトルや画像など様々なコンポーネントが含まれるのでその情報
  order: number //areaには複数のリンクユニットが含まれており、表示順などの影響を見るのに役に立ちます
  payload: { ... } // ユニットに特有の変数をJSONで格納します。例えば、『行動に基づくおすすめ』においては、レコメンドのロジックを格納したりしています。ただし100文字の制限があるため、超えないように配慮する必要があります
}

また、要素の表示イベントは .track-inview-by-gtm をトリガーの条件にして、そのエレメントに含めた data-gtm-track-json 属性のjsonの値から、GA4に送信するデータを抽出するようにしています。

このようにクリックイベントと要素の表示イベントで同じデータを参照するように設計しておくことで*3、真のCTRを出すなど詳細な分析を容易にします。

iframeで提供しているブログカードのクリックを計測する

はてなブログで愛用いただいている機能としてブログカード機能があります。
以下のようなものです。

これはiframeで提供しているため通常はブログカードのクリックを取得することができません。しかし、自身が紹介した外部ページや、過去記事へのリンクがどれくらいクリックされたのかは重要な情報です。よって、以下の方針でブログカードのクリックイベントも計測できるようにしました。

  • ブログカード内のリンククリックが発生した場合、親フレームに向けてmessageを送信しする
  • 親フレームではmessageを受け取り、そのデータを元にGTMのdataLayerにカスタムイベントをpushする
  • GTMではカスタムイベントをトリガーにクリックデータをGA4に送信する*4

データプロセス管理のためDataformを利用する

ブログチームでは、Dataformというデータプロセス管理ツールを利用しています。Dataformは、Googleに買収されGoogle Cloud のメンバーとして提供されており、安心して利用することができます。
Dataform is joining Google Cloud: Deploy data transformations with SQL in BigQuery

Dataformを利用することで、以下のようなメリットを享受することができています。

  • SQLをGitで管理する、Githubとの連携が可能
    • レビューやバージョン管理が容易になる
  • SQLに合わせてConfigが含められており、ViewやTableのDescriptionやカラムごとのDescriptionも合わせて管理できる
  • コードに含まれるConfigを1行変更するだけで View, Table を切り替えられる
    • 差分を更新したい場合には、Incremental Datasets という選択肢も取ることができます
  • クエリの定期実行も管理できる

例えば、GA4からエクスポートされたデータは、とても扱いにくい形式かつデータ量が多いため、サマリデータとして書き出しておきたいものです*5。例えば、以下のような設定を用意しておくと、ある程度使いやすく、ボリュームが抑えられたデータが用意できます。また、別のイベント(下記の例では読了率)の集計結果も付け加えておくなど、単にpage_viewを集計するだけではなく付加価値をつけることも可能です。

config {
  type: "incremental",
  schema: "blog__warehouse",
   bigquery: {
    partitionBy: "DATE_TRUNC(event_date, MONTH)"
  },
  description: "GA4のデータは多すぎるので、日付やURL、デバイスカテゴリなどよく使うディメンションでグループ化し、使いやすくしたものです。GA4のデータ連携の都合上、数日遅延することがあります。",
  columns: {
    event_date: "イベントが発生した日付",
    device_category: "desktop, mobile, tabletなど",
    medium: "organic, referralなど流入元の大まかな属性が記録されます",
    traffic_source: "流入元のドメインなどが記録されます",
    canonical_location: "canonical url",
    page_hostname: "hostname",
    page_view_count: "ページビュー数",
    unique_user_count: "ユニークユーザー数", 
    finish_reading_count: "読了数",
  },
  tags: ["warehouse"]
}
 
 WITH ga_page_view_data AS ( 
      SELECT
        user_pseudo_id,
        DATE(TIMESTAMP_MICROS(event_timestamp), "Japan") AS event_date,
        event_name,
        device.category AS device_category,
        traffic_source.medium AS medium,
        traffic_source.source AS traffic_source,
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "page_location") AS page_location,
 (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "canonical_location") AS canonical_location,
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "scrolled_position") AS scrolled_position,
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "track_area") AS track_area,
      FROM
        `blog-dwh.analytics_xxxxxxxx.events_*`
      WHERE
        _TABLE_SUFFIX = FORMAT_DATE("%Y%m%d" , DATE_SUB(CURRENT_DATE(), INTERVAL 4 DAY))
        AND event_name IN ('page_view', 'scroll')
        AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = "router_type") = "blogs"
      ORDER BY event_timestamp
)
SELECT
  event_date,
  device_category,
  medium,
  traffic_source,
  canonical_location,
  REGEXP_EXTRACT(page_location, r'https?://(.*?)/') AS page_hostname,
  COUNTIF(event_name = "page_view") AS page_view_count,
  COUNT(DISTINCT user_pseudo_id) AS unique_user_count,
  COUNTIF(event_name = "scroll" AND track_area = "finish_reading") AS finish_reading_count
FROM
  ga_page_view_data
${when(incremental(), `WHERE event_date > (SELECT MAX(event_date) FROM ${self()})`)}
GROUP BY
  event_date,
  device_category,
  page_hostname,
  medium,
  traffic_source,
  canonical_location

終わりに

ここまではてなブログのデータ活用について振り返ってきました。全体を通した感想を書いてみます。

  • データ活用は基盤を作って終わり、ではなくデータの文化や人の育成なども含めて整えていく必要がある
    • プランナー向けにデータの勉強会を実施したりはしているものの、まだまだできてない
  • ディレクターやプランナーがデータを扱えるようになるのには大きなメリットがある
    • サービスのプランナーはサービスの利用について仮説を持っているはずで、それを自分で検証できるのは強いし面白い
  • はてなのデータは面白い!
    • 自分自身、はてなブログ、はてなブックマーク、はてなスターなどはてなのサービスを大いに利用しているわけですが、それに関するデータを使ってサービス改善を目指せるのは本当に面白い
    • 他のプランナーを誘うときも「データ面白いよ」「せっかく最高の環境があるのにもったいない」ということをずっと言ってた

面白いとしか言っていませんね…。

ということで、まだまだ発展途上ではありますが、はてなブログでは急速にデータ活用が進んできており、大変面白い局面にあります。はてなではディレクター職を募集しておりますので、興味がある方は是非ご応募ください。
hatena.co.jp

*1:なお、はてなブックマークはこれより少し前から整備されていました

*2:SQLの例としてよく挙げられるクラス名簿のDBでは分析したい欲求が出ないのです

*3:GTMでは、data-gtm-track-json 属性を自動イベント変数として変数に定義しておくと、クリックイベントでも要素の表示イベントでもアクセスできるようになり、変数を使い回すことができます

*4:その際には、通常のクリックイベントと同様のカスタムディメンションを送信するようにしておく

*5:日付別シャーディングテーブルなど普段触らないので書き方をすぐに忘れてしまいます…