Skip to content

Instantly share code, notes, and snippets.

@letswritetw
Created March 29, 2025 08:50
Show Gist options
  • Save letswritetw/5bcebc7b1b4d405f4459fe8a871d4f2c to your computer and use it in GitHub Desktop.
Save letswritetw/5bcebc7b1b4d405f4459fe8a871d4f2c to your computer and use it in GitHub Desktop.
使用 Google Apps Script 串接 Google Analytics API,整合多站數據
var propertyId = "XXXXXXXXXX"; // 替換成 GA 的資源編號
var startDate = "2025-01-01"; // 替換成想要從哪一天開始抓的日期
var pageTitle = "XXXXXXXXX"; // 替換成想要篩選的頁面標題文字
// 瀏覽量、活躍人數
function getGA4Data() {
var apiUrl = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}:runReport`;
var payload = {
"dateRanges": [{
"startDate": startDate,
"endDate": "today"
}],
"metrics": [
{ "name": "screenPageViews" }, // 瀏覽數
{ "name": "activeUsers" } // 活躍用戶數
]
};
var options = {
"method": "post",
"contentType": "application/json",
"headers": {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
},
"muteHttpExceptions": true,
"payload": JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(apiUrl, options);
var data = JSON.parse(response.getContentText());
// 檢查回應中是否有 rows 資料
if (data.rows && data.rows.length > 0) {
// 取得瀏覽數和活躍用戶數
var screenPageViews = data.rows[0].metricValues[0].value;
var activeUsers = data.rows[0].metricValues[1].value;
// 回傳資料
return {
"totalPageViews": screenPageViews,
"activeUsers": activeUsers
};
} else {
// 若無符合資料,則回傳 0 作為預設值
return {
"totalPageViews": 0,
"activeUsers": 0
};
}
}
// 瀏覽量、活躍人數:指定頁面標題
function getGA4DataPage() {
var apiUrl = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}:runReport`;
var payload = {
"dateRanges": [{
"startDate": startDate,
"endDate": "today"
}],
"metrics": [
{ "name": "screenPageViews" }, // 瀏覽數
{ "name": "activeUsers" } // 活躍用戶數
],
"dimensionFilter": {
"filter": {
"fieldName": "unifiedScreenName",
"stringFilter": {
"value": pageTitle,
"matchType": "ENDS_WITH"
}
}
}
};
var options = {
"method": "post",
"contentType": "application/json",
"headers": {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
},
"muteHttpExceptions": true,
"payload": JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(apiUrl, options);
var data = JSON.parse(response.getContentText());
if (data.rows && data.rows.length > 0) {
var screenPageViews = data.rows[0].metricValues[0].value;
var activeUsers = data.rows[0].metricValues[1].value;
return {
"totalPageViews": screenPageViews,
"activeUsers": activeUsers
};
} else {
return {
"totalPageViews": 0,
"activeUsers": 0
};
}
}
// 即時人數
function getGA4RealtimeData() {
var apiUrl = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}:runRealtimeReport`;
var payload = {
"metrics": [{
"name": "activeUsers"
}]
};
var options = {
"method": "post",
"contentType": "application/json",
"headers": {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
},
"muteHttpExceptions": true,
"payload": JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(apiUrl, options);
var data = JSON.parse(response.getContentText());
// 取得即時人數
return data.rows[0].metricValues[0].value;
}
// 即時人數:指定頁面標題
function getGA4RealtimeDataPage() {
var apiUrl = `https://analyticsdata.googleapis.com/v1beta/properties/${propertyId}:runRealtimeReport`;
// 在 payload 中新增 dimensions 和 dimensionFilter
var payload = {
"metrics": [{
"name": "activeUsers"
}],
"dimensions": [{
"name": "unifiedScreenName"
}],
"dimensionFilter": {
"filter": {
"fieldName": "unifiedScreenName",
"stringFilter": {
"value": pageTitle,
"matchType": "ENDS_WITH"
}
}
}
};
var options = {
"method": "post",
"contentType": "application/json",
"headers": {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
},
"muteHttpExceptions": true,
"payload": JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(apiUrl, options);
var data = JSON.parse(response.getContentText());
var totalActiveUsers = 0;
// 累加所有匹配頁面的人數
if (data.rows && data.rows.length > 0) {
data.rows.forEach(function (row) {
totalActiveUsers += parseInt(row.metricValues[0].value, 10);
});
}
return totalActiveUsers.toString(); // 返回所有匹配頁面的總人數
}
// 回應錯誤訊息
function createErrorResponse(message, code) {
var errorResponse = {
success: false,
error: message,
code: code || 400,
timestamp: new Date().toISOString()
};
var jsonOutput = ContentService.createTextOutput(JSON.stringify(errorResponse));
jsonOutput.setMimeType(ContentService.MimeType.JSON);
return jsonOutput;
}
// 處理 POST
function doPost(e) {
// 確認有傳入內容
if (!e.postData || !e.postData.contents) {
return createErrorResponse("無效請求:未收到 POST 資料。" + e.postData);
}
// 將 POST 資料解析成 JSON
var requestData;
try {
requestData = JSON.parse(e.postData.contents);
} catch (error) {
return createErrorResponse("JSON 格式無效。");
}
// 檢查是否有 type 參數
if (!requestData.type) {
return createErrorResponse("缺少「type」參數。");
}
var output;
// 根據 type 參數執行不同的邏輯
switch (requestData.type) {
// 瀏覽量、活躍人數
case "data":
var reportData = getGA4Data();
output = {
"totalPageViews": reportData.totalPageViews,
"activeUsers": reportData.activeUsers
};
break;
// 瀏覽量、活躍人數:指定頁面標題
case "dataPage":
var reportData = getGA4DataPage();
output = {
"totalPageViews": reportData.totalPageViews,
"activeUsers": reportData.activeUsers
};
break;
// 即時人數
case "realtimeData":
var reportData = getGA4RealtimeData();
output = {
"activeUsers": reportData
};
break;
// 即時人數:指定頁面標題
case "realtimeDataPage":
var reportData = getGA4RealtimeDataPage();
output = {
"activeUsers": reportData
};
break;
default:
return createErrorResponse("type 錯誤");
}
// 回傳 JSON 格式的結果
var jsonOutput = ContentService.createTextOutput(JSON.stringify(output));
jsonOutput.setMimeType(ContentService.MimeType.JSON);
return jsonOutput;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment