スプレッドシートを用いた広告の配信設定(オン/オフ)変更

スクリプトの概要

Yahoo!広告スクリプトを用いてGoogleスプレッドシートと連携することにより、さまざまな広告の操作が可能となります。
本ページでは、Googleスプレッドシート上で1時間単位の配信開始日時と配信停止日時、広告名を指定し、広告の配信設定を切り替えるスクリプトを紹介します。
本スクリプトによる配信設定の切り替えが発生した場合は、メールとSlackでの通知も可能です。

必要な設定

スプレッドシートのヘッダーを以下のようにご設定ください。

スプレッドシート画像1

ステータスを切り替える対象の広告や、開始・終了日時を1行ずつご記入ください。

スプレッドシート画像2

ご注意

本スクリプトは、B列の「広告名」のセルに記載された単語を含む広告名の広告が切り替え対象となることにご留意ください。

例えば、上記スクリーンショット2行目の列であれば、以下の広告名を持つ広告すべてが切り替え対象となります。

  • 広告_A

  • 広告_A(1125停止)

  • New広告_A_20221121開始

また、スクリプト管理画面の「実行頻度」より当スクリプトが「1時間ごと」に実行されるようにご設定ください。

サンプルコード

サンプルコード内各定数について

・MAIL_TO
メールの送信先として利用したいYahoo! JAPANビジネスIDを、配列内に文字列として記載します。
例として、「test1111business」「example2222yahoo」の2つのビジネスIDに紐づくメールアドレスにメールを送信したい場合は以下のように記述します。

const MAIL_TO = ['test1111business', 'example2222yahoo'];

・URL_FETCH_APP
Slackにスクリプトからメッセージを送信する際に必要となる Incoming Webhook URL です。
こちらの取得方法や詳細についてはこちらのページをご確認ください。

・YOUR_USER_NAME
Slackメッセージの送信に用いられるSlackのユーザー名です。

・YOUR_CHANNEL_ID
Slackメッセージが送信されるチャンネルIDです。
こちらはSlackのチャンネル名と同一になります。

・TEST_EXECUTION
trueの場合はテスト実行が行われ、広告のステータスは変更されませんが、変更対象となる広告をログと通知メッセージにて確認することができます。
falseの場合は実際に広告のステータスが変更され、対象となった広告がログと通知メッセージに出力されます。

検索広告


/*
■スクリプト内容
スプレッドシートで指定した単語を含む広告名のものを、指定開始日時に開始時配信状態にし、指定終了日時に終了配信状態にします。
またオンオフ切り替えが発生したときのみ、メールまたはSlackで通知します。
■利用方法
1.当スクリプトを、検索広告のスクリプトとしてください。
2.各定数を以下のように設定してください。
■定数
・SPREAD_SHEET_ID:スプレッドシートID
・SPREAD_SHEET_NAME:スプレッドシート名
・FLAG_MAIL:結果をメール送信するならtrue、しないならfalse
・MAIL_TO:メール送信先のYahoo! BusinessID
・MAIL_TITLE:メールタイトル
・FLAG_SLACK:Slack配信するときはtrue、配信しないときはfalse
・URL_FETCH_APP:SlackのWebhook URL
・YOUR_USER_NAME:Slackのユーザー名
・YOUR_CHANNEL_ID:SlackのチャンネルID
・TEST_EXECUTION:テスト実行時はtrue、本番実行時はfalse
■表頭
以下のように記載し、2行目以降に各項目を記載してください
A列:アカウントID //ここでアカウントIDが一致した行のみを指定行とする
B列:広告名(を含む) //ここで記載の単語を含む広告名のものが配信オンオフの対象
C列:開始日付 //「yyyy/M/d」の形式で記載(例:2001/1/1、2022/12/31)
D列:開始時間 //「H:00」の形式で記載(例:1:00、13:00)
E列:開始時配信状態 //オンまたはオフを記載
F列:終了日付 //「yyyy/M/d」の形式で記載(例:2001/1/1、2022/12/31)
G列:終了時間 //「H:00」の形式で記載(例:1:00、13:00)
H列:終了時配信状態 //オンまたはオフを記載
■制限事項
同行で開始日時と終了日時が一致するとき、開始日時側を優先します。
同じ広告を対象とする指示が複数行に記載のとき、上行を優先します。
*/
const SPREAD_SHEET_ID = 'スプレッドシートID';
const SPREAD_SHEET_NAME = 'スプレッドシート名';
const FLAG_MAIL = false;
const MAIL_TO = ['Yahoo! BusinessID'];
let MAIL_TITLE = '【Yahoo Ads Script】広告配信切替';
const FLAG_SLACK = false;
const URL_FETCH_APP = 'Slack投稿先のFETCH';
const YOUR_USER_NAME = 'Slackのユーザ名';
const YOUR_CHANNEL_ID = 'SlackのチャンネルID';
const TEST_EXECUTION = true;
const accountId = AdsUtilities.getCurrentAccountId();
let TEXT_MESSAGE_ARRAY = [];
function main() {
  if (TEST_EXECUTION) {
    Logger.log('※テスト実行です。広告のステータスは実際には変更されません。');
  }
  //対象広告のオンオフを切り替える
  try {
    switchAds();
  } catch (e) {
    Logger.log(e);
    MAIL_TITLE += ' スクリプトが失敗しています';
    TEXT_MESSAGE_ARRAY.push('何らかのエラーによりスクリプトが失敗しました。');
  }
  //メール、Slack、もしくはその両方に通知
  if (TEXT_MESSAGE_ARRAY.length > 0) {
    if (TEST_EXECUTION) {
      TEXT_MESSAGE_ARRAY.unshift('※テスト実行です。広告のステータスは実際には変更されません。');
    }
    if (FLAG_MAIL) {
      sendMail();
    }
    if (FLAG_SLACK) {
      sendSlack();
    }
  }
}
//対象広告のオンオフを切り替える
function switchAds() {
  //条件指定シートをshとする
  let sh = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheetByName(SPREAD_SHEET_NAME);
  //スプレットシート情報を取得する
  let dataArray = sh.getDataRange().getValues();
  //もし2行以上の記載がなければ
  if (dataArray.length < 2) {
    logAndMessage(SPREAD_SHEET_NAME + 'シートに、広告のオンオフの部分一致条件を記載してください');
    return;
  }
  //dataArrayからadNamePartToSwitchを取得する
  let adNamePartToSwitch = getAdNamePartToSwitch(dataArray);
  if (Object.keys(adNamePartToSwitch).length == 0) return;
  //オンオフを変更する対象のoperandを取得する
  let adGroupAdArray = getAdGroupAdArray(adNamePartToSwitch);
  if (adGroupAdArray.length == 0) {
    Logger.log('スプレッドシートで指定した単語を含む、オンオフ切り替え対象の広告が存在しません');
    return;
  }
  //広告のオンオフを切り替える
  setAdGroupAds(adGroupAdArray);
}
//ログ、メールなどに流すテキスト
function logAndMessage(text) {
  Logger.log(text);
  TEXT_MESSAGE_ARRAY.push(text);
}
//dataArrayからadNamePartToSwitchを取得する
function getAdNamePartToSwitch(dataArray) {
  let dayString = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/M/d');
  let timeString = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'H:00');
  let adNamePartToSwitch = {};
  for (let i = 0; i < dataArray.length; i++) {
    //アカウントIDが異なるときは次へ
    if (Number(dataArray[i][0]) != accountId) continue;
    //広告名が空なら次へ
    let adNamePart = dataArray[i][1];
    if (adNamePart == '') continue;
    //すでに広告名がadNamePartToSwitchに登録されていれば次へ
    if (adNamePart in adNamePartToSwitch) continue;
    //開始日時が一致するなら
    if (dayString == dataArray[i][2] && timeString == dataArray[i][3]) {
      adNamePartToSwitch = addAdNamePartToSwitch(dataArray[i][4], adNamePartToSwitch, adNamePart, i + 1, '開始');
      Logger.log(i + 1 + '行目:' + adNamePart + ',' + dataArray[i][2] + ',' + dataArray[i][3] + ',' + dataArray[i][4]);
      //開始日時は一致しないが終了日時が一致するなら
    } else if (dayString == dataArray[i][5] && timeString == dataArray[i][6]) {
      adNamePartToSwitch = addAdNamePartToSwitch(dataArray[i][7], adNamePartToSwitch, adNamePart, i + 1, '終了');
      Logger.log(i + 1 + '行目:' + adNamePart + ',' + dataArray[i][5] + ',' + dataArray[i][6] + ',' + dataArray[i][7]);
    }
  }
  return adNamePartToSwitch;
}
//adNamePartToSwitchを更新する
function addAdNamePartToSwitch(switchString, adNamePartToSwitch, adNamePart, rowNum, startOrEndString) {
  if (switchString == 'オン') {
    adNamePartToSwitch[adNamePart] = 'ACTIVE';
  } else if (switchString == 'オフ') {
    adNamePartToSwitch[adNamePart] = 'PAUSED';
  } else {
    Logger.log(rowNum + '行目(' + adNamePart + ')の' + startOrEndString + '時配信状態を、オンまたはオフで指定してください');
  }
  return adNamePartToSwitch;
}
//オンオフを変更する対象のoperandを取得する
function getAdGroupAdArray(adNamePartToSwitch) {
  let adGroupAdArray = [];
  let num = 0;
  while (true) {
    const adGroupAds = Search.AdGroupAdService.get({
      accountId: accountId,
      numberResults: 10000,
      startIndex: num * 10000 + 1,
    }).rval;
    for (let i = 0; i < Object.keys(adGroupAds.values).length; i++) {
      let adGroupAd = adGroupAds.values[i].adGroupAd;
      for (let adNamePart in adNamePartToSwitch) {
        let newUserStatus = adNamePartToSwitch[adNamePart];
        //広告名にadNamePartが含まれていたら
        if (adGroupAd.adName.indexOf(adNamePart) >= 0) {
          //ステータスがadNamePartの指定ステータスと異なるとき
          if (newUserStatus != adGroupAd.userStatus) {
            adGroupAdArray.push({
              campaignId: adGroupAd.campaignId,
              campaignName: adGroupAd.campaignName,
              adGroupId: adGroupAd.adGroupId,
              adGroupName: adGroupAd.adGroupName,
              adId: adGroupAd.adId,
              adName: adGroupAd.adName,
              userStatus: newUserStatus,
            });
            //ステータスがadNamePartの指定ステータスと同じとき
          } else {
            if (newUserStatus == 'ACTIVE') {
              logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'はすでに配信オンです');
            } else {
              logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'はすでに配信オフです');
            }
          }
          break;
        }
      }
    }
    num++;
    if (num * 10000 >= adGroupAds.totalNumEntries) break;
  }
  return adGroupAdArray;
}
//広告のオンオフを切り替える
function setAdGroupAds(adGroupAdArray) {
  if (!TEST_EXECUTION) {
    let num = 0;
    while (true) {
      const adGroupAds = Search.AdGroupAdService.set({
        accountId: accountId,
        operand: adGroupAdArray.slice(num * 2000, Math.min((num + 1) * 2000, adGroupAdArray.length)),
      }).rval;
      for (let i = 0; i < Object.keys(adGroupAds.values).length; i++) {
        if (adGroupAds.values[i].operationSucceeded) {
          let adGroupAd = adGroupAdArray[num * 2000 + i];
          if (adGroupAd.userStatus == 'ACTIVE') {
            //TEXT_MESSAGE_ARRAY.push(text)となっておりログ出力されなかったのを修正
            logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'を配信オンにしました');
          } else {
            logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'を配信オフにしました');
          }
        } else {
          Logger.log(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'の配信切替に失敗しました');
        }
      }
      num++;
      if (num * 2000 >= adGroupAdArray.length) break;
    }
  } else {
    for (const ad of adGroupAdArray) {
      const newUserStatus = ad.userStatus == 'ACTIVE' ? 'オン' : 'オフ';
      logAndMessage(ad.campaignName + '/' + ad.adGroupName + '/' + ad.adName + 'を配信' + newUserStatus + 'にしました');
    }
  }
}
//メール送信
function sendMail() {
  MailApp.sendEmail({
    to: MAIL_TO,
    subject: MAIL_TITLE,
    body: TEXT_MESSAGE_ARRAY.join('\n'),
  });
}
//Slackメッセージ送信
function sendSlack() {
  UrlFetchApp.fetch(URL_FETCH_APP,
    {
      method: 'POST',
      contentType: 'application/json',
      payload: {
        text: TEXT_MESSAGE_ARRAY.join('\n'),
        username: YOUR_USER_NAME,
        channel: YOUR_CHANNEL_ID,
      }
    });
}

ディスプレイ広告


/*
■スクリプト内容
スプレッドシートで指定した単語を含む広告名のものを、指定開始日時に開始時配信状態にし、指定終了日時に終了配信状態にします。
またオンオフ切り替えが発生したときのみ、メールまたはSlackで通知します。
■利用方法
1.当スクリプトを、ディスプレイ広告のスクリプトとしてください。
2.各定数を以下のように設定してください。
■定数
・SPREAD_SHEET_ID:スプレッドシートID
・SPREAD_SHEET_NAME:スプレッドシート名
・FLAG_MAIL:結果をメール送信するならtrue、しないならfalse
・MAIL_TO:メール送信先のYahoo! BusinessID
・MAIL_TITLE:メールタイトル
・FLAG_SLACK:Slack配信するときはtrue、配信しないときはfalse
・URL_FETCH_APP:SlackのWebhook URL
・YOUR_USER_NAME:Slackのユーザー名
・YOUR_CHANNEL_ID:SlackのチャンネルID
・TEST_EXECUTION:テスト実行時はtrue、本番実行時はfalse
■表頭
以下のように記載し、2行目以降に各項目を記載してください
A列:アカウントID //ここでアカウントIDが一致した行のみを指定行とする
B列:広告名(を含む) //ここで記載の単語を含む広告名のものが配信オンオフの対象
C列:開始日付 //「yyyy/M/d」の形式で記載(例:2001/1/1、2022/12/31)
D列:開始時間 //「H:00」の形式で記載(例:1:00、13:00)
E列:開始時配信状態 //オンまたはオフを記載
F列:終了日付 //「yyyy/M/d」の形式で記載(例:2001/1/1、2022/12/31)
G列:終了時間 //「H:00」の形式で記載(例:1:00、13:00)
H列:終了時配信状態 //オンまたはオフを記載
■制限事項
同行で開始日時と終了日時が一致するとき、開始日時側を優先します。
同じ広告を対象とする指示が複数行に記載のとき、上行を優先します。
*/
const SPREAD_SHEET_ID = 'スプレッドシートID';
const SPREAD_SHEET_NAME = 'スプレッドシート名';
const FLAG_MAIL = false;
const MAIL_TO = ['Yahoo! BusinessID'];
let MAIL_TITLE = '【Yahoo Ads Script】広告配信切替';
const FLAG_SLACK = false;
const URL_FETCH_APP = 'Slack投稿先のFETCH';
const YOUR_USER_NAME = 'Slackのユーザ名';
const YOUR_CHANNEL_ID = 'SlackのチャンネルID';
const TEST_EXECUTION = true;
const accountId = AdsUtilities.getCurrentAccountId();
let TEXT_MESSAGE_ARRAY = [];
function main() {
  if (TEST_EXECUTION) {
    Logger.log('※テスト実行です。広告のステータスは実際には変更されません。');
  }
  //対象広告のオンオフを切り替える
  try {
    switchAds();
  } catch (e) {
    Logger.log(e);
    MAIL_TITLE += ' スクリプトが失敗しています';
    TEXT_MESSAGE_ARRAY.push('何らかのエラーによりスクリプトが失敗しました。');
  }
  //メール、Slack、もしくはその両方に通知
  if (TEXT_MESSAGE_ARRAY.length > 0) {
    if(TEST_EXECUTION) {
      TEXT_MESSAGE_ARRAY.unshift('※テスト実行です。広告のステータスは実際には変更されません。');
    }
    if (FLAG_MAIL) {
      sendMail();
    }
    if (FLAG_SLACK) {
      sendSlack();
    }
  }
}
//対象広告のオンオフを切り替える
function switchAds() {
  //条件指定シートをshとする
  let sh = SpreadsheetApp.openById(SPREAD_SHEET_ID).getSheetByName(SPREAD_SHEET_NAME);
  //スプレットシート情報を取得する
  let dataArray = sh.getDataRange().getValues();
  //もし2行以上の記載がなければ
  if (dataArray.length < 2) {
    logAndMessage(SPREAD_SHEET_NAME + 'シートに、広告のオンオフの部分一致条件を記載してください');
    return;
  }
  //dataArrayからadNamePartToSwitchを取得する
  let adNamePartToSwitch = getAdNamePartToSwitch(dataArray);
  if (Object.keys(adNamePartToSwitch).length == 0) return;
  //オンオフを変更する対象のoperandを取得する
  let adGroupAdArray = getAdGroupAdArray(adNamePartToSwitch);
  if (adGroupAdArray.length == 0) {
    Logger.log('スプレッドシートで指定した単語を含む、オンオフ切り替え対象の広告が存在しません');
    return;
  }
  //広告のオンオフを切り替える
  setAdGroupAds(adGroupAdArray);
}
//ログ、メールなどに流すテキスト
function logAndMessage(text) {
  Logger.log(text);
  TEXT_MESSAGE_ARRAY.push(text);
}
//dataArrayからadNamePartToSwitchを取得する
function getAdNamePartToSwitch(dataArray) {
  let dayString = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/M/d');
  let timeString = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'H:00');
  let adNamePartToSwitch = {};
  for (let i = 0; i < dataArray.length; i++) {
    //アカウントIDが異なるときは次へ
    if (Number(dataArray[i][0]) != accountId) continue;
    //広告名が空なら次へ
    let adNamePart = dataArray[i][1];
    if (adNamePart == '') continue;
    //すでに広告名がadNamePartToSwitchに登録されていれば次へ
    if (adNamePart in adNamePartToSwitch) continue;
    //開始日時が一致するなら
    if (dayString == dataArray[i][2] && timeString == dataArray[i][3]) {
      adNamePartToSwitch = addAdNamePartToSwitch(dataArray[i][4], adNamePartToSwitch, adNamePart, i + 1, '開始');
      Logger.log(i + 1 + '行目:' + adNamePart + ',' + dataArray[i][2] + ',' + dataArray[i][3] + ',' + dataArray[i][4]);
      //開始日時は一致しないが終了日時が一致するなら
    } else if (dayString == dataArray[i][5] && timeString == dataArray[i][6]) {
      adNamePartToSwitch = addAdNamePartToSwitch(dataArray[i][7], adNamePartToSwitch, adNamePart, i + 1, '終了');
      Logger.log(i + 1 + '行目:' + adNamePart + ',' + dataArray[i][5] + ',' + dataArray[i][6] + ',' + dataArray[i][7]);
    }
  }
  return adNamePartToSwitch;
}
//adNamePartToSwitchを更新する
function addAdNamePartToSwitch(switchString, adNamePartToSwitch, adNamePart, rowNum, startOrEndString) {
  if (switchString == 'オン') {
    adNamePartToSwitch[adNamePart] = 'ACTIVE';
  } else if (switchString == 'オフ') {
    adNamePartToSwitch[adNamePart] = 'PAUSED';
  } else {
    Logger.log(rowNum + '行目(' + adNamePart + ')の' + startOrEndString + '時配信状態を、オンまたはオフで指定してください');
  }
  return adNamePartToSwitch;
}
//オンオフを変更する対象のoperandを取得する
function getAdGroupAdArray(adNamePartToSwitch) {
  let adGroupAdArray = [];
  let num = 0;
  while (true) {
    const adGroupAds = Display.AdGroupAdService.get({
      accountId: accountId,
      numberResults: 10000,
      startIndex: num * 10000 + 1,
    }).rval;
    for (let i = 0; i < Object.keys(adGroupAds.values).length; i++) {
      let adGroupAd = adGroupAds.values[i].adGroupAd;
      for (let adNamePart in adNamePartToSwitch) {
        let newUserStatus = adNamePartToSwitch[adNamePart];
        //広告名にadNamePartが含まれていたら
        if (adGroupAd.adName.indexOf(adNamePart) >= 0) {
          //ステータスがadNamePartの指定ステータスと異なるとき
          if (newUserStatus != adGroupAd.userStatus) {
            adGroupAdArray.push({
              campaignId: adGroupAd.campaignId,
              campaignName: adGroupAd.campaignName,
              adGroupId: adGroupAd.adGroupId,
              adGroupName: adGroupAd.adGroupName,
              adId: adGroupAd.adId,
              adName: adGroupAd.adName,
              userStatus: newUserStatus,
            });
            //ステータスがadNamePartの指定ステータスと同じとき
          } else {
            if (newUserStatus == 'ACTIVE') {
              logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'はすでに配信オンです');
            } else {
              logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'はすでに配信オフです');
            }
          }
          break;
        }
      }
    }
    num++;
    if (num * 10000 >= adGroupAds.totalNumEntries) break;
  }
  return adGroupAdArray;
}
//広告のオンオフを切り替える
function setAdGroupAds(adGroupAdArray) {
  if (!TEST_EXECUTION) {
    let num = 0;
    while (true) {
      const adGroupAds = Display.AdGroupAdService.set({
        accountId: accountId,
        operand: adGroupAdArray.slice(num * 2000, Math.min((num + 1) * 2000, adGroupAdArray.length)),
      }).rval;
      for (let i = 0; i < Object.keys(adGroupAds.values).length; i++) {
        if (adGroupAds.values[i].operationSucceeded) {
          let adGroupAd = adGroupAdArray[num * 2000 + i];
          if (adGroupAd.userStatus == 'ACTIVE') {
            //TEXT_MESSAGE_ARRAY.push(text)となっておりログ出力されなかったのを修正
            logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'を配信オンにしました');
          } else {
            logAndMessage(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'を配信オフにしました');
          }
        } else {
          Logger.log(adGroupAd.campaignName + '/' + adGroupAd.adGroupName + '/' + adGroupAd.adName + 'の配信切替に失敗しました');
        }
      }
      num++;
      if (num * 2000 >= adGroupAdArray.length) break;
    }
  } else {
    for (const ad of adGroupAdArray) {
      const newUserStatus = ad.userStatus == 'ACTIVE' ? 'オン' : 'オフ';
      logAndMessage(ad.campaignName + '/' + ad.adGroupName + '/' + ad.adName + 'を配信' + newUserStatus + 'にしました');
    }
  }
}
//メール送信
function sendMail() {
  MailApp.sendEmail({
    to: MAIL_TO,
    subject: MAIL_TITLE,
    body: TEXT_MESSAGE_ARRAY.join('\n'),
  });
}
//Slackメッセージ送信
function sendSlack() {
  UrlFetchApp.fetch(URL_FETCH_APP,
    {
      method: 'POST',
      contentType: 'application/json',
      payload: {
        text: TEXT_MESSAGE_ARRAY.join('\n'),
        username: YOUR_USER_NAME,
        channel: YOUR_CHANNEL_ID,
      }
    });
}