GA4のData APIで複数のフィルターをかけながらスプレッドシートに自動連携する
業務効率化公開日:2024年8月28日 更新日:2024年8月29日
Google Analytics4(以下GA4)のデータをスプレッドシートに出力する方法は様々ですが、かゆいところに手が届かない感じがしませんか?
私の場合、複数のフィルターをかけることが多いので、そのたびにコードを書き換えることがかなり手間でした。
なのでこの記事では、GA4のデータをスプレッドシートに出力する際に、「複数のフィルターをかける」ことにフォーカスして解説をします。
また、この記事ではGA4のデータをスプレッドシートに出力する方法について、具体的には解説しないので、「まず連携したい」という場合は、以下の記事を参考にしてください。
目次
Data APIでのフィルターのかけ方
Data APIのフィルターの種類
まずフィルターをかけるためには、Data APIにどのようなフィルターがあるのかを理解する必要があります。
Data APIではFilterExpressionというフィールドで、指標またはディメンションに対してフィルターをかけます。
そしてまずは以下の3つから、どのようなフィルターをかけるのかを指定する必要があります。
- andGroup
- orGroup
- notExpression
文字列から推測できると思うのですが、andGroupはAND条件、orGroupはOR条件、そしてnotExpressionはNOT条件です。
Groupとしているように、これらはグループ化して扱います。
この3つを決めた上で、次に扱うデータタイプを決める必要があります。
- stringFilter
- inListFilter
- numericFilter
- betweenFilter
stringFilterでは文字列を、inListFilterではリスト形式のものを、numericFilterでは数値や日付を、そしてbetweenFilterでは2つの値を用います。
高度なレポートを作る場合はこれらのものを使い分けるのですが、例えばデイリーレポートを作成するような目的であればstringFilterとnumericFilterくらいしか使いません。
今回提供するコードではAND条件、NOT条件、そしてstingFilterに限定しています。
さらに、かけたいフィルターの文字列や数値を、どのように一致させるかも指定する必要があります。
- EXACT:~~と完全一致
- BEGINS_WITH:~~で始まる
- ENDS_WITH:~~で終わる
- CONTAINS:~~を含む
- FULL_REGEXP:正規表現が完全一致する
- PARTIAL_REGEXP:正規表現が部分一致する
これも、正直良く使うのはEXACT、CONTAINSで、たまにFULL_REGEXPを使う程度なので全てを覚える必要はありません。
殆どの場合EXACT(完全一致)とCONTAINS(含む)だけで、かけたいフィルターは実現できると思います。
フィルターをかけるディメンションを確認する
さて、次にフィルターをかけるディメンションを決めなくてはなりません。
Data APIで利用できる指標はこちらで確認することができます。
例えばセッションの参照元/メディアだけに絞りたいという場合は、「sessionSourceMedium」になります。
今回は複数のフィルターをかける解説なので、他の指標も例として挙げておきましょう。
以下のディメンションを今回は用います。
- セッションの参照元/メディア:sessionSourceMedium
- ホスト名:hostName
フィルターをかけるための準備
フィルターをかけるための関数の解説
複数のフィルターをかける場合、FilterExpressionに則り、必要な分だけフィールドを用意する必要があります。
そのため1つ1つフィルターをかけていくと、コードが冗長になるため、フィルターを生成するための関数を用意しました。
function createFilter(fieldName, value, matchType = 'EXACT', negate = '') {
const filterExpression = AnalyticsData.newFilterExpression();
filterExpression.filter = AnalyticsData.newFilter();
filterExpression.filter.fieldName = fieldName;
filterExpression.filter.stringFilter = AnalyticsData.newStringFilter();
filterExpression.filter.stringFilter.value = value;
filterExpression.filter.stringFilter.matchType = matchType;
if (negate === 'negative') {
const notExpression = AnalyticsData.newFilterExpression();
notExpression.notExpression = filterExpression;
return notExpression;
}
return filterExpression;
}
引数は以下の4つを用意しています。
- filedName
- value
- matchType
- negate
まずfieldNameは、設定するディメンションを指定します。
このコードはstringFilterを実装するものであるため、指標は指定できません。
次にvalueはディメンションに対して、どんな文字列でフィルターをかけるかです。
例えばsessionSourceMediumを「google / cpc」としたい場合、そのままvalueとして「google / cpc」と指示します。
matchTypeは先述した「EXACT」や「CONTAINS」を指定します。初期値をEXACTとしているので、指定しないこともできます。
最後にnegateは、それをNOT条件にするか否かです。
もしそのフィルターをNOT条件にしたい場合は「negative」という文字列を引数として設定します。(True or Falseにしようかなと思ったんですが、わかりにくいかなと思って・・・)
negateも初期値を空白に設定しています。
関数の呼び出し方
さて引数を理解したところで、この関数の呼び出し方です。
プログラミングが分からないという方向けの解説です。
まずディメンションを「sessionSourceMedium」、値を「google / cpc」、そしてマッチタイプを「完全一致」とする場合、以下のように関数を呼び出します。
createFilter('sessionSourceMedium', 'google / cpc'),
先述の関数名はcreateFilterなので、createFilterを宣言した上で、()内に、順番に引数を指定していきます。
matchTypeとnegateは初期値を設定しているため任意項目ですが、filedNameとvalueは必須項目です。
次の例は、ディメンションを「sessionSourceMedium」、値を「yahoo」、そしてマッチタイプを「含む」とする場合、以下のように関数を呼び出します。
createFilter('sessionSourceMedium', 'yahoo', 'CONTAINS')
最後に、ディメンションを「hostName」、値を「stg.digimanavi.com」、マッチタイプを「完全一致」、そしてNOT条件にしたい場合、以下のようになります。
createFilter('hostName', 'stg.digimanavi.com', 'EXACT', 'negative')
このように簡単にフィルター設定をできるようになったのが、この関数のミソです。
関数の返り値を格納して、フィルターとして設定する
最後に関数を呼び出した上で、これらをfieldAndという変数に格納します。
以下の例では2つのフィルターを設定しています。
const filtersAnd = [
createFilter('sessionSourceMedium', 'google / cpc'),
createFilter('hostName', 'stg.digimanavi.com', 'EXACT', 'negative')
];
あとは、Data APIを実行する時に、このfilterAndが呼び出されるように設定をするだけです。
const dimensionFilter = AnalyticsData.newFilterExpression();
dimensionFilter.andGroup = AnalyticsData.newFilterExpressionList();
dimensionFilter.andGroup.expressions = filtersAnd;
これでフィルターの設定が完了です。
コードの全体像
冒頭紹介した記事で紹介したコードに、今回紹介したコードを付け加えたものが以下のものです。
function runGaReport() {
// Setting
const propertyId = '*******';
const wb = SpreadsheetApp.openById('*******');
const ws = wb.getSheetByName('*******');
// Create Metric & Dimension
function createMetric(name) {
const metric = AnalyticsData.newMetric();
metric.name = name;
return metric;
}
function createDimension(name) {
const dimension = AnalyticsData.newDimension();
dimension.name = name;
return dimension;
}
function createFilter(fieldName, value, matchType = 'EXACT', negate = '') {
const filterExpression = AnalyticsData.newFilterExpression();
filterExpression.filter = AnalyticsData.newFilter();
filterExpression.filter.fieldName = fieldName;
filterExpression.filter.stringFilter = AnalyticsData.newStringFilter();
filterExpression.filter.stringFilter.value = value;
filterExpression.filter.stringFilter.matchType = matchType;
if (negate === 'negative') {
const notExpression = AnalyticsData.newFilterExpression();
notExpression.notExpression = filterExpression;
return notExpression;
}
return filterExpression;
}
// Setup Metric & Dimension
const metrics = ['sessions', 'activeUsers', 'engagedSessions'].map(createMetric);
const dimensions = ['date', 'sessionSource'].map(createDimension);
// Setup Data Range
const dateRange = AnalyticsData.newDateRange();
dateRange.startDate = '2023-01-01';
dateRange.endDate = 'yesterday';
// Setup Filters
const filtersAnd = [
createFilter('sessionSourceMedium', 'google / cpc'),
createFilter('hostName', 'stg.digimanavi.com', 'EXACT', 'negative')
];
const dimensionFilter = AnalyticsData.newFilterExpression();
dimensionFilter.andGroup = AnalyticsData.newFilterExpressionList();
dimensionFilter.andGroup.expressions = filtersAnd;
// Run Report
const request = AnalyticsData.newRunReportRequest();
request.dimensions = dimensions;
request.metrics = metrics;
request.dateRanges = [dateRange];
request.dimensionFilter = dimensionFilter;
try {
const report = AnalyticsData.Properties.runReport(request, `properties/${propertyId}`);
if (!report.rows) {
console.log('No rows returned.');
return;
}
//ws clear
ws.clear();
// Process headers
const dimensionHeaders = report.dimensionHeaders.map(header => header.name);
const metricHeaders = report.metricHeaders.map(header => header.name);
const headers = [...dimensionHeaders, ...metricHeaders];
ws.appendRow(headers);
// Process and append rows
const rows = report.rows.map(row => {
const dimensionValues = row.dimensionValues.map(value => {
if (/^\d{8}$/.test(value.value)) {
return `${value.value.substring(0, 4)}/${value.value.substring(4, 6)}/${value.value.substring(6, 8)}`;
}
return value.value;
});
const metricValues = row.metricValues.map(value => value.value);
return [...dimensionValues, ...metricValues];
});
ws.getRange(2, 1, rows.length, headers.length).setValues(rows);
console.log('Report Updated!');
} catch (e) {
console.log('Failed with error: %s', e.error);
}
}
ぜひみなさん、こちらのコードを使って業務効率化を進めてみてください。