Google Analytics Data APIで特定時間の記事ごとのページビュー数を取得する(GA4)

 2023年8月31日

当サイトの「注目の記事ランキング」は、Google Analytics から API 経由で取得したデータをベースに算出しているわけですが、その中で、GA4 から時刻 x URL ごとの PageViews(ページビュー)を取得する機会があったので、自分用メモ。

まずは、GA4 の REST リファレンスの実行レポート(runReport)ページ(v1beta)

の右側「Try this method」で試せる「Request body」向けの JSON 形式で書いておきます。

  "dateRanges": [
    {
      "startDate": "3daysAgo",
      "endDate": "today",
      "name": "new_page_views"
    }
  ],
  "dimensions": [
    {
      "name": "pagePath"
    }
  ],
  "metrics": [
    {
      "name": "screenPageViews"
    }
  ],
  "dimensionFilter": {
    "andGroup": {
      "expressions": [
        {
          "filter": {
            "fieldName": "pagePath",
            "stringFilter": {
              "matchType": "FULL_REGEXP",
              "value": "~^/archives/\\d.*/$",
              "caseSensitive": false
            }
          },
          "filter": {
            "fieldName": "dateHour",
            "inListFilter": {
              "values": [
                "2023071313",
                "2023071312"
              ],
              "caseSensitive": false
            }
          }          
        }
      ]
    }
  },
  "metricFilter": {
    "filter": {
      "fieldName": "screenPageViews",
      "numericFilter": {
        "operation": "GREATER_THAN",
        "value": {
          "int64Value": 3
        }
      }
    }
  },
  "orderBys": [
    {
      "desc": true,
      "metric": {
        "metricName": "screenPageViews"
      }
    }
  ],
  "limit":300Code language: JSON / JSON with Comments (json)

上記の例では、

  • pagePath が正規表現 "~^/archives/\d.*/$" にマッチ
  • 2023年7月13日 13時台
  • 2023年7月13日 12時台
  • 上記時間帯の合計PVが4以上

の pagePath 毎の PV を集計しています。

dateRanges は必須らしいので無理やり付けてあります。また、dateRanges の範囲内に dateHour が収まっている必要があるので、適宜、日付周りは調整してください。

最初は dimensionFilter で betweenFilter を使おうとしていましたが、エラーが出て使えないため代替案として inListFilter で抽出対象の YYYYMMDDHH を全列挙する方針を取っています。

時刻の範囲が広いなら、多分、dateHour を stringFilter の正規表現で絡め取ってもイケるんじゃないかな。

手元の環境では、現在時刻から3~4時間前台以前くらいからデータ取得できるようになる様子だけど、Universal Analytics と違って GA4 では若い時刻の PV が半分くらいしか取れていないような。(気のせい?)

リアルタイム性の高いデータ抽出が必要なら、リアルタイムレポートの併用とかも考えた方が良いかもしんない。

caseSensitive はわざと無意味な記述も残してあります。不要なら削除で。

stringFilter の MatchType と、numericFilter の operation は、runReport ページのテストベッド上では文字列表記も通る。対応する enum の値は巻末の資料を参照。

なお、PHP でこれを記述する場合、項目名がけっこう変わる。先の JSON の API コールと同一内容かは知らねども、Google Analytics Data API の PHP ソースを漁りながら書くハメになったので、ついでなので PHP での記述例もメモしておく。(Google Analytics Data API - V1beta)

// Authentication JSON
putenv("GOOGLE_APPLICATION_CREDENTIALS=" . __DIR__ . '/YOUR_GA4_API-XXXXXXXXXXXX.json');

require 'vendor/autoload.php';

use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;
use Google\Analytics\Data\V1beta\DateRange;
use Google\Analytics\Data\V1beta\Dimension;
use Google\Analytics\Data\V1beta\Metric;
use Google\Analytics\Data\V1beta\NumericValue;
use Google\Analytics\Data\V1beta\Filter;
use Google\Analytics\Data\V1beta\Filter\InListFilter;
use Google\Analytics\Data\V1beta\Filter\StringFilter;
use Google\Analytics\Data\V1beta\Filter\NumericFilter;
use Google\Analytics\Data\V1beta\FilterExpression;
use Google\Analytics\Data\V1beta\FilterExpressionList;
use Google\Analytics\Data\V1beta\OrderBy;
use Google\Analytics\Data\V1beta\OrderBy\MetricOrderBy;

// Your Google Analytics 4 property ID.
$property_id = 'XXXXXXXXX';

// API client.
// The credentials are used as specified in the environment
// variable "GOOGLE_APPLICATION_CREDENTIALS"
$client = new BetaAnalyticsDataClient();

// Make an API call.
$response = $client->runReport([
    'property' => 'properties/' . $property_id,
    'dateRanges' => [
        new DateRange([
            'start_date' => '3daysAgo',
            'end_date' => 'today',
        ]),
    ],
    'dimensions' => [
        new Dimension([
            'name' => 'pagePath',
        ]),
    ],
    'metrics' => [
	new Metric([
            'name' => 'screenPageViews',
        ]),
    ],
    'dimensionFilter' =>
        new FilterExpression([
            'and_group' => new FilterExpressionList([
                'expressions' => [
                    new FilterExpression([
                        'filter' => new Filter([
                            'field_name' => 'pagePath',
                            'string_filter' => new StringFilter([
                                'match_type' => Filter\StringFilter\MatchType::FULL_REGEXP,  // FULL_REGEXP = 5
                                'value' => "^/YOUR/\d.*/REGEX$",
                            ]),
                        ]),
                    ]),
                    new FilterExpression([
                        'filter' => new Filter([
                            'field_name' => 'dateHour',
                            'in_list_filter' => new InListFilter([
                                'values' => [ '2023071311', '2023071310', '2023071309', '2023071308' ],
                            ]),
                        ]),
                    ])
                ],
            ]),
        ]),
    'metricFilter' => 
        new FilterExpression([
	    'filter' => new Filter([
	        'field_name' => 'screenPageViews',
		    'numeric_filter' => new NumericFilter([
                        'operation' => Filter\NumericFilter\Operation::GREATER_THAN,  // GREATER_THAN  = 4
                        'value' => new NumericValue([
                        'int64_value' => 3
                    ]),
                ]),
            ]),
        ]),
    'orderBys' => [
        new OrderBy([
            'metric' => new MetricOrderBy([
	        'metric_name' => 'screenPageViews',
	    ]),
            'desc' => true,
        ]),
    ],	
]);

// Print results of an API call.
print 'Report result: ' . PHP_EOL;

foreach ($response->getRows() as $row) {
    print $row->getDimensionValues()[0]->getValue()
        . ' ' . $row->getMetricValues()[0]->getValue() . PHP_EOL;
}
Code language: PHP (php)

余談ですが、当サイトのように速報性もないのに PV 多めで安定しているサイトでバカ正直に集計すると、閲覧数ランキングは固定化してしまいがちで。

その他にも様々な理由からランキングが変動しやすい集計ロジックを採用しているので、だから「PV ランキング」じゃなくて、「注目の記事ランキング」という名前にしているのでした。

関連資料:

Hatena Pocket Line

コメントを記入