審査落ち広告の通知

更新履歴

2024/10/04 以下の修正を実施しました
       ・出力モード「APPEND」の削除(結果的に「REPLACE」のみとなり、フラグ自体を削除)
        理由: 審査落ちから承認を経て、再び審査落ちした広告が通知対象外となる問題があったため
        補足: スプレッドシートへの出力は正常に行われていました
       ・通知の頻度「ON_ADDED」の修正
        理由: 2回目以降の広告追加の有無に関わらず通知されてしまうため
        補足: 修正前でもスプレッドシートへの出力は正常に行われています

スクリプトの概要

本ページでは、審査落ちした広告があった場合に、該当の広告の情報をGoogleスプレッドシートへ一覧で出力し、出力結果をメールまたはSlackで通知できるスクリプトをご紹介します。

本機能をご利用いただくことで、広告管理ツール上で確認するしかなかった審査落ち理由も含めてGoogleスプレッドシート上で一覧で確認できるため、広告の審査結果の確認工数が減少し運用の効率化が図れます。

※当スクリプトは検索広告・ディスプレイ広告共通版となっております。(設定されたアカウントが検索広告かディスプレイ広告かを自動で判定します)

広告の審査状況のステータスについて

審査状況のステータスについてについては、こちらのヘルプ(検索広告/ディスプレイ広告)をご覧ください。

【Googleスプレッドシートへ出力される項目】
以下の項目が出力されます。
日(スクリプト実行日)
キャンペーンID
キャンペーン名
広告グループID
広告グループ名
広告ID
広告名
配信設定
広告タイプ
審査状況
審査落ち理由
最終リンク先URL

ご利用の流れ

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

2.連携したGoogleアカウントにて、審査落ち広告を出力するGoogleスプレッドシートを新規作成してください。
 作成したスプレッドシートIDを確認してメモしておいてください。スクリプト内の定数の設定で利用します。
 ※GoogleスプレッドシートIDの取得方法についてはこちらをご覧ください。

3.管理画面上のスクリプト作成画面にて、後述のサンプルコードを設定してください。
 ※スクリプトの新規作成および編集の手順はこちらをご覧ください。

4.管理画面上にてスクリプトの設定を完了後、必要に応じてスクリプトの実行頻度の設定をしてください。
本スクリプトでは1日1回(時間は任意)の設定が推奨です。
 ※スクリプトの実行頻度の設定手順はこちらをご覧ください。

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

後述のサンプルコードにおける各定数の設定方法についてご説明いたします。

■スプレッドシートID、シート名の指定例

次の例のように、' '(シングルクオーテーション)で囲んで指定してください。(スプレッドシートID:'12345abcde'、シート名:'Sheet1'の場合)
const SPREAD_SHEET_ID = '12345abcde'; 
const SHEET_NAME = 'Sheet1';

■チェック対象の広告の配信状況について

審査落ちをチェックする対象とする広告の配信状況を指定可能です。ご利用状況に合わせていずれかをご指定ください。
const TARGET_AD_STATUS = 'BOTH';// BOTH:両方、ACTIVE:配信オンのみ、PAUSED:配信オフのみ

■通知の頻度の指定について

スクリプトの実行結果を通知する頻度を指定できます。ご利用状況に合わせていずれかをご指定ください。
通知の頻度 解説
ON_ADDED 前回のスクリプト実行後、追加で審査落ち広告がある場合のみ通知します
ALWAYS スクリプトの実行後、毎回通知します
NONE 通知自体しません
const ALERT_TYPE = 'ON_ADDED';// ON_ADDED:追加がある場合のみ、ALWAYS:毎回実行後、NONE:通知自体しない

ご注意

通知の頻度を「NONE」にしていても、スクリプトの実行エラーの場合はメール/Slack通知されます。
実行エラーの場合も通知が不要の場合は、メール、Slackの指定例を参考にFLAG_MAILとFLAG_SLACKを両方falseにしてください。

■メール、Slackの指定例

メール、Slack設定についてをご確認ください。

サンプルコード

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

/*このソースコードは MIT License のもとで提供されています。
https://ads-developers.yahoo.co.jp/ja/ads-script/post/30418913.html 
■スクリプト内容
アカウントに、実行時点で指定した配信状況の審査落ちの広告があった場合にメール/Slack通知します。
■利用方法
1.当スクリプトを、検索広告またはディスプレイ広告のアカウントに設定してください。
2.定数を以下のように設定してください。
■定数
・SPREAD_SHEET_ID   //スプレットシートIDを指定
・SHEET_NAME        //出力シート名
・TARGET_AD_STATUS  //抽出対象とする広告の配信状況。BOTH:両方、ACTIVE:配信オンのみ、PAUSED:配信オフのみ。
・ALERT_TYPE        //通知の頻度。ON_ADDED:追加がある場合のみ、ALWAYS:毎回実行後、NONE:通知自体しない。
                      ※NONEにしていても、実行エラーの場合はメール/Slack通知されます。
                       実行エラーの場合も通知しなくていい場合は、FLAG_MAILとFLAG_SLACKを両方falseにしてください。
・FLAG_MAIL         //結果をメール送信するならtrue、しないならfalse
・MAIL_TO           //メール送信先のYahoo! BusinessID
・MAIL_TITLE        //メールタイトル
・FLAG_SLACK        //結果をSlack送信するならtrue、しないならfalse
・URL_FETCH_APP     //SlackのWebhook URL
■制限事項
・取得できる審査落ちの広告は、1万件が上限となります。
 */
//設定が必要な定数
const SPREAD_SHEET_ID = 'SPREAD_SHEET_ID';
const SHEET_NAME = 'SHEET_NAME';
const TARGET_AD_STATUS = 'BOTH';// BOTH:両方、ACTIVE:配信オンのみ、PAUSED:配信オフのみ
const ALERT_TYPE = 'ON_ADDED';// ON_ADDED:追加がある場合のみ、ALWAYS:毎回実行後、NONE:通知自体しない
const FLAG_MAIL = false;
const MAIL_TO = ['Yahoo! JAPAN Business ID'];
let MAIL_TITLE = '審査落ち広告通知';
const FLAG_SLACK = false;
const URL_FETCH_APP = 'SLACK_WEBHOOK_URL';
//設定が不要な定数(変更するとエラーになります)
const accountId = AdsUtilities.getCurrentAccountId();
const productType = AdsUtilities.getProductType();
const productObj = productType == 'SEARCH' ? Search : Display;
let TEXT_MESSAGE_ARRAY = [];
const SPREAD_SHEET_URL = 'https://docs.google.com/spreadsheets/d/' + SPREAD_SHEET_ID;
const SS_HEADER = ['日', 'キャンペーンID', 'キャンペーン名', '広告グループID', '広告グループ名',
  '広告ID', '広告名', '配信設定', '広告タイプ', '審査状況', '審査落ち理由', '最終リンク先URL'];
const adIdColIdx = 5;//並び順変えたとき対策
function main() {
  try {
    if (productType == 'MCC') {
      throw new Error('このスクリプトはMCCアカウントでは実行できません');
    }
    //審査落ち広告を取得
    const disapprovalAds = getDisapprovalAds();
    if (disapprovalAds != null && disapprovalAds.length > 0) {
      //対象がある場合のみ出力・通知
      const addCnt = outputDisapprovalAdsToSS(disapprovalAds);
      if (ALERT_TYPE == 'ALWAYS'//毎回通知は常に通知
        || (ALERT_TYPE == 'ON_ADDED' && addCnt > 0)//追加がある場合のみ通知
      ) {
        sendMailAndSlack();
      }
    } else {
      Logger.log('審査落ち広告はありませんでした。');
    }
  } catch (error) {
    MAIL_TITLE = '!!エラー発生!!' + MAIL_TITLE;
    logAndMessage('!!エラーが発生しました!!詳細は管理画面のログをご確認ください。' + error);
    sendMailAndSlack();
    throw Error(error);
  }
}
//マスタから審査落ち広告を取得
function getDisapprovalAds() {
  const operand = {
    accountId: accountId,
    numberResults: 10000,
    approvalStatuses: [
      'PRE_DISAPPROVED', 'POST_DISAPPROVED',
    ],
    userStatuses: [TARGET_AD_STATUS]
  };
  if (TARGET_AD_STATUS == 'BOTH') {
    //両方の場合はuserStatusesで絞らない
    delete operand.userStatuses;
  }
  const adGroupAds = productObj.AdGroupAdService.get(operand).rval.values;
  Logger.log('対象データを取得しました');
  return adGroupAds;
}
//スプレッドシートに審査落ち広告を出力する
function outputDisapprovalAdsToSS(disapprovalAds) {
  //出力用データ生成
  let outputData = createOutputData(disapprovalAds);
  Logger.log('出力データ生成が完了しました');
  //出力
  const ss = validateAndGetSpreadsheet();
  const sh = validateAndGetSheet(ss);
  const previousSSData = sh.getDataRange().getValues();//前回データ
  //ヘッダを付ける前のデータ行だけを比較する
  const addCnt = comparePreviousAndCurrent(previousSSData, outputData);
  logAndMessage('前回からの追加は' + addCnt + '件です。');
  //洗い替え
  sh.clear();
  outputData.unshift(SS_HEADER);//ヘッダ行を先頭に追加
  sh.getRange('A1').setValues(outputData);
  logAndMessage('スプレッドシートにデータを出力しました。\n' + SPREAD_SHEET_URL);
  return addCnt;
}
//出力データを生成する
function createOutputData(disapprovalAds) {
  const disapprovalReasonDictArr = getDisapprovalReasons();
  const today = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd');
  let outputData = [];
  for (let i = 0; i < disapprovalAds.length; i++) {
    const adGroupAd = disapprovalAds[i].adGroupAd;
    let titleArray = [];
    for (let j = 0; j < adGroupAd.disapprovalReasonCodes.length; j++) {
      titleArray.push(disapprovalReasonDictArr[adGroupAd.disapprovalReasonCodes[j]]);
    }
    const adType = adGroupAd.ad.adType;
    const adTypeStr = productType == 'SEARCH' ? getSearchAdTypeNameByAdType(adType) : getDisplayAdTypeNameByAdType(adType);
    outputData.push([
      today,
      adGroupAd.campaignId,
      adGroupAd.campaignName,
      adGroupAd.adGroupId,
      adGroupAd.adGroupName,
      adGroupAd.adId,
      adGroupAd.adName,
      (adGroupAd.userStatus == 'ACTIVE') ? 'オン' : 'オフ',
      adTypeStr,
      getApprovalStatusStr(adGroupAd.approvalStatus),
      titleArray.join(','),
      adGroupAd.ad.finalUrl
    ]);
  }
  return outputData;
}
//審査落ち理由をDictionaryServiceから取得する
function getDisapprovalReasons() {
  const dictionaries = productObj.DictionaryService.getDisapprovalReason({
    lang: 'JA',
  }).rval;
  let dictionaryArray = [];
  for (let i = 0; i < dictionaries.totalNumEntries; i++) {
    let dictionary = dictionaries.values[i].disapprovalReason;
    dictionaryArray[dictionary.disapprovalReasonCode] = dictionary.title;
  }
  return dictionaryArray;
}
//(検索広告)広告タイプから広告タイプ名を取得
function getSearchAdTypeNameByAdType(adType) {
  const adTypeNames = {
    'EXTENDED_TEXT_AD': '拡大テキスト広告',
    'DYNAMIC_SEARCH_LINKED_AD': '動的検索連動型広告',
    'TEXT_AD2': 'テキスト広告',
    'APP_AD': 'アプリ訴求広告',
    'RESPONSIVE_SEARCH_AD': 'レスポンシブ検索広告'
  };
  return adTypeNames[adType] || '新しい広告タイプ';
}
//(ディスプレイ広告)広告タイプから広告タイプ名を取得
function getDisplayAdTypeNameByAdType(adType) {
  const adTypeNames = {
    'BANNER_AD': 'バナー広告',
    'CAROUSEL_AD': 'カルーセル広告',
    'DYNAMIC_DISPLAY_AD': '動的ディスプレイ広告',
    'RESPONSIVE_AD': 'レスポンシブ広告',
    'TEXT_AD': 'テキスト広告'
  };
  return adTypeNames[adType] || '新しい広告タイプ';
}
//審査状況の文字列生成
function getApprovalStatusStr(approvalStatus) {
  switch (approvalStatus) {
    case 'PRE_DISAPPROVED':
      return '掲載不可';
    case 'POST_DISAPPROVED':
      return '掲載停止';
    default://ないとは思うが
      return '';
  }
}
//前回と今回を比較して追加された数を取得する
function comparePreviousAndCurrent(previousSSData, outputData) {
  previousSSData.shift()
  const previousAdIdSet = createAdIdSet(previousSSData);//前回スプシ内容全件なので、ヘッダは抜いておく
  const currentAdIdSet = createAdIdSet(outputData);
  //比較
  let addCnt = 0
  currentAdIdSet.forEach(function (currentAdId) {
    if (!previousAdIdSet.has(currentAdId)) {
      addCnt++;
    }
  })
  return addCnt;
}
//広告IDのSetを生成する
function createAdIdSet(ssData) {
  let adIdSet = new Set();//重複排除するためSetを使う
  for (let i = 0; i < ssData.length; i++) {
    const adId = Number(ssData[i][adIdColIdx]);
    adIdSet.add(adId);
  }
  return adIdSet;
}
//スプレッドシート部品
function validateAndGetSpreadsheet() {
  if (SPREAD_SHEET_ID === 'SPREAD_SHEET_ID' || SPREAD_SHEET_ID === '') {
    throw new Error('スプレッドシートIDを設定してください。');
  }
  try {
    return SpreadsheetApp.openById(SPREAD_SHEET_ID);
  } catch (e) {
    throw new Error('スプレッドシートを開くことが出来ませんでした。スプレッドシートIDまたはスプレッドシートの権限が正しいか確認してください。' + e);
  }
}
function validateAndGetSheet(ss) {
  if (SHEET_NAME === '') {
    throw new Error('シート名を設定してください。');
  }
  const sh = ss.getSheetByName(SHEET_NAME);
  if (sh === null) {
    throw new Error('シートが開けませんでした。シート名を確認してください。');
  }
  return sh;
}
//ログ、メールなどに流すテキスト
function logAndMessage(text) {
  Logger.log(text);
  TEXT_MESSAGE_ARRAY.push(text);
}
//メール・Slack部品
function sendMailAndSlack() {
  validateAndSendSlack();
  validateAndSendMail();
}
function validateAndSendSlack() {
  if (FLAG_SLACK) {
    if (URL_FETCH_APP === 'SLACK_WEBHOOK_URL' || URL_FETCH_APP === '') {
      throw new Error('SlackのWebhook URLを設定してください。Slack送信前までに実行された処理は完了しています。');
    }
    UrlFetchApp.fetch(URL_FETCH_APP, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify({
        text:
          '【アカウントID:' + accountId + '】' + MAIL_TITLE + '\n' +//Slackもタイトルがあった方がいい
          TEXT_MESSAGE_ARRAY.join('\n'),
      }),
    });
  }
}
function validateAndSendMail() {
  if (FLAG_MAIL) {
    if (MAIL_TO.length < 1 || !MAIL_TO.every(str => typeof str === 'string' && /^[a-zA-Z0-9]+$/.test(str))) {
      throw new Error('Yahoo! JAPANビジネスIDを設定してください。メール送信前までに実行された処理は完了しています。');
    }
    MailApp.sendEmail({
      to: MAIL_TO,
      subject: '【アカウントID:' + accountId + '】' + MAIL_TITLE,
      body: TEXT_MESSAGE_ARRAY.join('\n')
    });
  }
}

結果確認

審査落ちした広告があった場合に、Googleスプレッドシートに結果が出力されていれば成功です。