事例集(その他)

Q.Google App Scriptでスプレッドシートの行を削除するdeleteRows()と同等の機能は提供されていますか?
Q.データ分析で使うため、毎日キャンペーンの入札戦略と目標値を出力したいのですが、可能ですか?
Q.AccountService.get()で一度に取れる件数は500件までだと思いますが、それ以上取りたい場合はどうすればよいですか?

Q.Google App Scriptでスプレッドシートの行を削除するdeleteRows()と同等の機能は提供されていますか?


A.deleteRow()及び、deleteRows()の提供はありません。下記に同等の機能を再現した関数をご用意しましたので、ご参考にいただければと思います。

動作可能プロダクト:共通
動作確認バージョン:v202406
 コードサンプル ここをクリックすると折り畳みます。

function main() {
  const ss = SpreadsheetApp.openById('スプレッドシートID');
  const sh = ss.getSheetByName('シート名');
  deleteRows(sh, 3, 2);
}
/**
 * 指定したシートから指定した範囲の行を削除する関数。
 * SpreadsheetApp.deleteRowsを使用せず、getValuesとsetValuesを使用して行を削除します。
 *
 * @param {SheetObject} sheet - 操作対象のシート名。
 * @param {number} startRow - 削除を開始する行番号(1から始まる)。
 * @param {number} numRows - 削除する行数。
 */
function deleteRows(sheet, startRow, numRows){
  const lastRow = sheet.getLastRow();
  const lastColumn = sheet.getLastColumn();
  const data = sheet.getRange(1, 1, lastRow, lastColumn).getValues();
  const endRow = startRow + numRows - 1;
  const newData = [];
  for (let i = 0; i < data.length; i++) {
    if (i < startRow - 1 || i >= endRow) {
      newData.push(data[i]);
    }
  }
  sheet.clear();
  if (newData.length > 0) {
    sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
  }
}

Q.データ分析で使うため、毎日キャンペーンの入札戦略と目標値を出力したいのですが、可能ですか?


A.はい、以下のスクリプトで実現できます。

動作可能プロダクト:検索広告
動作確認バージョン:v202406
 コードサンプル ここをクリックすると折り畳みます。

/*このソースコードは MIT License のもとで提供されています。
https://ads-developers.yahoo.co.jp/ja/ads-script/post/30418913.html 
■スクリプト内容
スプレッドシートに、実行時点でのキャンペーンの入札戦略の設定を出力します。
■利用方法
1.当スクリプトを、検索広告のアカウントに設定してください。
2.定数を以下のように設定してください。
■定数
・SPREAD_SHEET_ID   //スプレットシートIDを指定
・OUTPUT_SHEET_NAME //出力シート名
・FLAG_MAIL         //結果をメール送信するならtrue、しないならfalse
・MAIL_TO           //メール送信先のYahoo! BusinessID
・MAIL_TITLE        //メールタイトル
・FLAG_SLACK        //結果をSlack送信するならtrue、しないならfalse
・URL_FETCH_APP     //SlackのWebhook URL
■制限事項
・ヘッダ行は出力しないので、ご自身でスプレッドシートに記載をお願いいたします。
 日  キャンペーンID	キャンペーン名	入札戦略	目標値
 */
//設定が必要な定数
const SPREAD_SHEET_ID = 'SPREAD_SHEET_ID';
const SHEET_NAME = 'SHEET_NAME'
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();
let TEXT_MESSAGE_ARRAY = [];
const SPREAD_SHEET_URL = '\nhttps://docs.google.com/spreadsheets/d/' + SPREAD_SHEET_ID;
function main() {
  try {
    const ss = validateAndGetSpreadsheet(SPREAD_SHEET_ID);
    //出力用データ生成
    const outputData = createOutputData();
    //スプレッドシートに出力
    const outputSh = validateAndGetSheet(ss);
    const lastRow = outputSh.getLastRow();
    outputSh.getRange('A' + (lastRow + 1)).setValues(outputData);//追記
    logAndMessage('スプレッドシートに' + outputData.length + '件のデータを出力しました');
    //通知
    validateAndSendMail();
    validateAndSendSlack();
  } catch (error) {
    MAIL_TITLE = '!!エラー発生!!' + MAIL_TITLE;
    logAndMessage('エラーが発生しました。詳細は管理画面のログをご確認ください。\n' + error);
    validateAndSendMail();
    validateAndSendSlack();
    throw error;//ログ上でエラーにするため再スロー
  }
}
function createOutputData() {
  //キャンペーンマスタ取得
  const campaignValues = getCampaignMaster();
  //出力用データ生成
  const todayStr = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd');
  let outputData = [];
  for (let i = 0; i < campaignValues.length; i++) {
    const campaign = campaignValues[i].campaign;
    const biddingInfo = getBiddingInfo(campaign.biddingStrategyConfiguration);
    if (biddingInfo.biddingStrategyStr == '') {
      throw new Error('キャンペーンID:' + campaign.campaignId
        + ' の入札戦略の取得に失敗しました。biddingStrategyConfiguration:'
        + JSON.stringify(campaign.biddingStrategyConfiguration));
    }
    outputData.push([
      todayStr,
      campaign.campaignId,
      campaign.campaignName,
      biddingInfo.biddingStrategyStr,
      biddingInfo.biddingValue
    ]);
  }
  Logger.log('出力データを' + outputData.length + '件作成しました');
  return outputData;
}
function getCampaignMaster() {
  const campaigns = Search.CampaignService.get({
    accountId: accountId,
    numberResults: 10000,//念のため最大値
  }).rval;
  if (campaigns.totalNumEntries == 0) {
    throw new Error('アカウント内にキャンペーンが存在しません。');
  }
  return campaigns.values;
}
//入札戦略・値の出力用文字列を生成する{biddingStrategyStr:入札戦略名 ,biddingValue:値}
//生成できなかった場合は、それぞれ空文字になる。呼び出し元でエラーハンドルする。
function getBiddingInfo(biddingStrategyConfiguration) {
  const biddingScheme = biddingStrategyConfiguration.biddingScheme;
  const biddingStrategyType = biddingScheme.biddingStrategyType;
  let biddingStrategyStr = '';
  let biddingValue = '-';
  switch (biddingStrategyType) {
    case 'CPC':
      biddingStrategyStr = '個別クリック単価';
      if (biddingScheme.cpcBiddingScheme.enhancedCpcEnabled == 'TRUE') {
        biddingStrategyStr += '(拡張クリック単価)';
      }
      biddingValue = '-';
      break;
    case 'TARGET_ROAS':
      biddingStrategyStr = '広告費用対効果の目標値';
      biddingValue = toPercentage(biddingScheme.targetRoasBiddingScheme.targetRoas);
      break;
    case 'MAXIMIZE_CLICKS':
      biddingStrategyStr = 'クリック数の最大化';
      if (biddingScheme.maximizeClicksBiddingScheme.bidCeiling == 0) {
        biddingValue = '-';
      } else {
        biddingStrategyStr += '(上限値を指定する)';
        biddingValue = biddingScheme.maximizeClicksBiddingScheme.bidCeiling + '円';
      }
      break;
    case 'TARGET_CPA':
      biddingStrategyStr = 'コンバージョン単価の目標値';
      biddingValue = biddingScheme.targetCpaBiddingScheme.targetCpa + '円';
      break;
    case 'MAXIMIZE_CONVERSIONS':
      biddingStrategyStr = 'コンバージョン数の最大化';
      if (biddingScheme.maximizeConversionsBiddingScheme.targetCpa == null) {
        biddingValue = '-';
      } else {
        biddingStrategyStr += '(目標値を指定する)';
        biddingValue = biddingScheme.maximizeConversionsBiddingScheme.targetCpa + '円';
      }
      break;
    case 'MAXIMIZE_CONVERSION_VALUE':
      biddingStrategyStr = 'コンバージョン価値の最大化';
      if (biddingScheme.maximizeConversionValueBiddingScheme.targetRoas == null) {
        biddingValue = '-';
      } else {
        biddingStrategyStr += '(目標値を指定する)';
        biddingValue = toPercentage(biddingScheme.maximizeConversionValueBiddingScheme.targetRoas);
      }
      break;
    case 'TARGET_IMPRESSION_SHARE':
      biddingStrategyStr = 'ページ最上部掲載';
      biddingValue = biddingScheme.targetImpressionShareScheme.bidCeiling + '円';
      break;
  }
  return { biddingStrategyStr: biddingStrategyStr, biddingValue: biddingValue };
}
function toPercentage(decimal) {
  if (typeof decimal !== 'number' || isNaN(decimal)) {
    throw new Error('Invalid input: the argument must be a number.');
  }
  return (decimal * 100).toFixed(2) + '%';
}
//ログ、メールなどに流すテキスト
function logAndMessage(text) {
  Logger.log(text);
  TEXT_MESSAGE_ARRAY.push(text);
}
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 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') + SPREAD_SHEET_URL,
    });
  }
}
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: TEXT_MESSAGE_ARRAY.join('\n') + SPREAD_SHEET_URL,
      }),
    });
  }
}

Q.AccountService.get()で一度に取れる件数は500件までだと思いますが、それ以上取りたい場合はどうすればよいですか?


A.以下のスクリプトのように、while文を使って500件ずつ取るようにしてください。
動作可能プロダクト:MCC(本スクリプトは検索広告のアカウントのみ)
動作確認バージョン:v202402
 コードサンプル ここをクリックすると折り畳みます。

function main(){
  let startIndex = 0;
  const maxNumberResults = 500;
  while (true) {
    const accounts = Search.AccountService.get({
      accountStatuses: ['SERVING'],
      numberResults: maxNumberResults,
      startIndex: startIndex * maxNumberResults + 1,
    }).rval;
    for (let i = 0; i < accounts.values.length; i++) {
      let account = accounts.values[i].account;
      Logger.log('ID:' + account.accountId + ' 名称:' + account.accountName);
    }
    startIndex++;
    if (accounts.totalNumEntries <= startIndex * maxNumberResults) break;
  }
}