MCC配下のレポート出力(ジェネレータ対応版)

更新履歴

2025/02/06 新規作成

スクリプトの概要

本スクリプトは、MCC配下の各アカウントの任意のレポートをGoogleスプレッドシートに出力します。
初期値では、検索広告のレポートを出力する設定となっています。ジェネレータで出力したレポート設定に内容を変更が可能なため、ご自身で出力したい任意のレポートをMCCアカウントに対して実行が可能となっています。

ご注意

このスクリプトでは、配信設定がオンになっているアカウントの実績のみが出力されます。

Yahoo!広告スクリプトの実行処理時間には10分以内と言う制限があるため、MCCに紐づくアカウント数が多すぎる場合はレポート出力処理が完了せずに実行が中断されてしまいます(タイムアウト)。これにより、MCC配下の全てのアカウントのレポート出力が完了しない場合は、以下の対策をお客様側で行っていただき、タイムアウトを回避していただくようお願いいたします。
・MCCを分ける
・集計期間を減らすなど出力量を減らす

必要な設定

1.Yahoo!広告スクリプトとGoogleアカウントを連携します
 詳しくはGoogleアカウントとの連携を確認してください。
 ※すでに連携済みの方は次のステップからご設定ください。

2.スプレッドシートIDを取得します
 詳しくはGoogleスプレッドシートIDの取得方法をご覧ください。

3.管理画面上のスクリプト作成画面にて、サンプルコードを設定してください。
 ▼サンプルコードをそのままご利用される場合
 管理画面上のスクリプト作成画面にて、後述のサンプルコードを設定してください。
 ▼ジェネレータで作成したスクリプトと差し替える場合
 ジェネレータの出力結果との差替方法を参考に、後述のサンプルコードの一部を差し替えて設定してください。
 ※スクリプトの新規作成および編集の手順はこちらをご覧ください。

4.管理画面上にてスクリプトの設定を完了後、スクリプトの実行頻度の設定をしてください。
 ※スクリプトの実行頻度の設定手順はこちらをご覧ください。

サンプルコード内各定数のご説明

後述のサンプルコードにおける各定数の設定方法についてご説明いたします。内容にあわせて変更を行ってください。

■スプレッドシートID、シート名

「必要な設定」で準備したスプレッドシートIDとシート名を指定します。

例)スプレッドシートID:'12345abcde'、シート名:'Sheet1'の場合
const SPREAD_SHEET_ID = '12345abcde'; //★書き出すスプレッドシートID。例:'SPREAD_SHEET_ID'
const SHEET_NAME = 'Sheet1';//★スプレッドシートのシート名。例:'シート1'

■レポート出力対象のプロダクト種別

レポート出力対象のプロダクト種別を指定してください。
SEARCH の場合は検索広告アカウント、DISPLAY の場合はディスプレイ広告アカウントのみ出力します。
※初期値は SEARCH となっています。

const USER_SPECIFIED_OUTPUT_TYPE = 'SEARCH';// SEARCH or DISPLAY

ジェネレータの出力結果との差替方法

ジェネレータの出力結果と差し替えます。

1.ジェネレータで任意のスクリプトを生成します。
ヘッダー出力及び合計行出力は「しない」を選択してください。

2.生成されたスクリプトをコピーします。
生成されたスクリプトから、下記部分のみコピーします。
※コピー箇所は、「function getReportData(accountId){」から最終行までです。
 すべてコピーするとスクリプトは動作しません。


3.本ページのサンプルコードの一部を2でコピーしたスクリプトと差し替えます。
サンプルコードをスクリプトの入力画面に貼り付け、下記部分を差し替えます。
39行目〜51行目にあたります。

補足

・スプレッドシートIDとスプレッドシート名の指定(20~21行目)を忘れないようにしてください
ジェネレータでスクリプト作成をする際に選んだプロダクト種別に合わせて、レポート出力対象のプロダクト種別の定数を変更してください。

サンプルコード

下記のスクリプトを、「コピー」ボタンを押してコピーし、スクリプトの入力画面に貼り付けてください。(このとき、灰色のコメント部分は消さずに残しておいてください)
「サンプルコード内各定数のご説明」またはスクリプト内に記載の利用方法に沿って、設定が必要な定数を設定してください。

/*
■スクリプト内容
MCCに紐づくアカウントに対してレポートをスプレッドシートに出力します。
関数getReportData()はジェネレーターで生成したものに差し替えが可能です。
■利用方法
1.当スクリプトを、ルートMCCアカウントまたはMCCアカウントのスクリプトとしてください。
2.各定数を以下のように設定してください。
・SPREAD_SHEET_ID:スプレッドシートID
・SPREAD_SHEET_NAME:シート名
・USER_SPECIFIED_OUTPUT_TYPE:レポート出力対象のプロダクト種別を指定してください。
                      'SEARCH':検索広告アカウント 'DISPLAY':ディスプレイ広告アカウント
                      ジェネレーターで生成したスクリプトがどちらのプロダクトかによって変更してください
・INCLUDE_MULTILEVEL_MCC:MCC複数階層の場合、設定されたMCCアカウントの直下のアカウントだけでなく、複数階層化された一番下のアカウントまで取得するかどうか
                         trueの場合、複数階層化されたすべてのアカウントを取得します。
                         falseの場合、設定されたMCCアカウント直下のアカウントのみ取得します。
■制限事項
このスクリプトでは、配信設定がオンになっているアカウントの実績のみが出力されます。
*/
//設定が必要な定数
const SPREAD_SHEET_ID = 'スプレッドシートID';
const SHEET_NAME = 'シート名';
const USER_SPECIFIED_OUTPUT_TYPE = 'SEARCH';// SEARCH or DISPLAY
//設定が任意の定数(必要に応じて変更してください)
const INCLUDE_MULTILEVEL_MCC = true;
//設定が不要な定数(変更するとエラーになります)
const thisMccAccountId = AdsUtilities.getCurrentAccountId();
const PRODUCT_TYPE = {
  Search: Search,
  Display: Display
};
const OUTPUT_TYPE = {
  Search: 'SEARCH',
  Display: 'DISPLAY'
};
const ACCOUNT_TYPE = {
  MCC: 'MCC',
  ADS: '通常'
};
function getReportData(accountId){
  return AdsUtilities.getSearchReport({
    accountId: accountId,
    fields: [
      'ACCOUNT_ID', 'ACCOUNT_NAME', 'IMPS', 'CLICKS', 'COST', 'AVG_CPC', 'CONVERSIONS', 'COST_PER_CONV', 'CONV_VALUE'
    ],
    reportDateRangeType: 'THIS_MONTH',
    reportType: 'ACCOUNT',
    reportSkipColumnHeader: "TRUE",
    reportSkipReportSummary: "TRUE",
    reportName: 'GENERATOR_20250121'
  }).reports[0].rows;
}
function main() {
  Logger.log('開始');
  if (!Object.values(OUTPUT_TYPE).includes(USER_SPECIFIED_OUTPUT_TYPE)) {
    throw new Error('USER_SPECIFIED_OUTPUT_TYPE には、SEARCH または DISPLAY の値を指定してください');
  }
  writeToSpreadsheet();
}
//YSAレポートをスプレッドシートに書き込み
function writeToSpreadsheet() {
  let ss = validateAndGetSpreadsheet();
  let sh = validateAndGetSheet(ss);
  sh.clear();//前処理でシートクリア
  //MCC内のYSA用のaccountIdsを取得する
  if (USER_SPECIFIED_OUTPUT_TYPE == OUTPUT_TYPE.Search ) {
    getReportAndOutput(sh, OUTPUT_TYPE.Search, PRODUCT_TYPE.Search);
  //MCC内のYDA用のaccountIdsを取得する
  } else if (USER_SPECIFIED_OUTPUT_TYPE == OUTPUT_TYPE.Display ) {
    getReportAndOutput(sh, OUTPUT_TYPE.Display, PRODUCT_TYPE.Display);
  }
}
function getReportAndOutput(sh, adType, productType) {
  let adAccountIds = getTargetAccountIds(productType, adType);
  if (adAccountIds.length > 0) {
    let reportData = getReport(adAccountIds);
    Logger.log('getReport完了');
    if (reportData.length > 0) {
      let startRow = (adType === OUTPUT_TYPE.Search) ? 1 : sh.getLastRow() + 1;
      sh.getRange(startRow, 1, reportData.length, reportData[0].length).setValues(reportData);
      Logger.log(adType + '広告アカウント' + adAccountIds.length + '件のレポートをスプレッドシートに出力しました');
    } else {
      Logger.log(adType + '広告アカウントで配信オンのレポートデータはありませんでした');
    }
  }
}
function validateAndGetSpreadsheet() {
  if (SPREAD_SHEET_ID === 'SPREAD_SHEET_ID' || SPREAD_SHEET_ID === '') {
    throw new Error('スプレッドシートIDを設定してください。');
  }
  return SpreadsheetApp.openById(SPREAD_SHEET_ID);
}
function validateAndGetSheet(ss) {
  if (SHEET_NAME === '') {
    throw new Error('シート名を設定してください。');
  }
  return ss.getSheetByName(SHEET_NAME);
}
function getTargetAccountIds(productType, adType) {
  let targetAccountIds = [];
  const mccChildAccountIds = getMccChildAccountIds(thisMccAccountId, productType);
  Logger.log('スクリプトを設定したMCC直下の' + adType + 'アカウント:' + JSON.stringify(mccChildAccountIds));
  if (mccChildAccountIds.length == 0) return targetAccountIds;
  if (INCLUDE_MULTILEVEL_MCC) {
    //複数階層化対応
    targetAccountIds = classifyAccountsByType(mccChildAccountIds, productType);
  } else {
    //直下だけ取ればよい(=従来どおりの挙動)
    targetAccountIds = getFilteredAccountIds(mccChildAccountIds, productType, ACCOUNT_TYPE.ADS);
  }
  Logger.log(adType + 'レポート取得対象アカウント:' + JSON.stringify(targetAccountIds));
  return targetAccountIds;
}
//引数のアカウントIDを、通常のアカウントとMCCアカウントに分け、一番下の階層まで深掘りする
function classifyAccountsByType(accountIds, productType) {
  //普通の広告アカウントかつ配信オン
  let adsAccountIds = getFilteredAccountIds(accountIds, productType, ACCOUNT_TYPE.ADS);
  Logger.log('普通の広告アカウント:' + adsAccountIds);
  //MCCの場合
  const mccAccountIds = getFilteredAccountIds(accountIds, productType, ACCOUNT_TYPE.MCC);
  for (let i = 0; i < mccAccountIds.length; i++) {
    Logger.log('MCCアカウント:' + mccAccountIds[i]);
    const childAccounts = getMccChildAccountIds(mccAccountIds[i], productType);
    Logger.log('MCCアカウント:' + mccAccountIds[i] + ' のchildAccounts:' + JSON.stringify(childAccounts));
    if (childAccounts.length > 0) {
      const grandChildAccount = classifyAccountsByType(childAccounts, productType);
      adsAccountIds = adsAccountIds.concat(grandChildAccount);
    }
  }
  return adsAccountIds;
}
//MCC内のaccountIdsを取得する
function getMccChildAccountIds(mccAccountId, productType) {
  let accountIds = [];
  let num = 0;
  while (true) {
    const accountLinks = productType.AccountLinkService.get({
      mccAccountId: mccAccountId,
      accountStatuses: ['SERVING'],
      numberResults: 500,
      startIndex: num * 500 + 1,
    }).rval;
    if (accountLinks.totalNumEntries == 0) break;
    for (let i = 0; i < accountLinks.values.length; i++) {
      accountIds.push(accountLinks.values[i].accountLink.accountId);
    }
    num++;
    if (num * 500 >= accountLinks.totalNumEntries) break;
  }
  return accountIds;
}
function getFilteredAccountIds(accountIds, productType, accountType) {
  const includeMccAccount = accountType == ACCOUNT_TYPE.MCC ? 'ONLY_MCC_ACCOUNT' : 'ONLY_ADS_ACCOUNT';
  let filterdAccountIds = [];
  let num = 0;
  while (true) {
    const accounts = productType.AccountService.get({
      accountIds: accountIds.slice(num * 200, Math.min((num + 1) * 200, accountIds.length)),
      includeMccAccount: includeMccAccount,
    }).rval;
    if (accounts.totalNumEntries == 0) break;
    for (let i = 0; i < accounts.values.length; i++) {
      let account = accounts.values[i].account;
      if (accountType == ACCOUNT_TYPE.ADS && account.deliveryStatus != 'ACTIVE') {
        continue;//通常の広告アカウントの場合は、オフの場合リストに追加せずskip
      }
      filterdAccountIds.push(account.accountId);
    }
    num++;
    if (num * 200 >= accountIds.length) break;
  }
  return filterdAccountIds;
}
//レポートを取得
function getReport(accountIds) {
  let reportData = [];
  for (let i = 0; i < accountIds.length; i++) {
    //レポートをreportDataにマージする
    const data = getReportData(accountIds[i]);
    if( data.length > 0 ){
      reportData = reportData.concat(data);
    }
  }
  return reportData;
}