天気予報レポート出力

スクリプトの概要

概要

本ページでは、パフォーマンスレポート(配信実績)とYahoo!天気・災害の天気予報データを連動させたレポートを
Googleスプレッドシートへ出力し、出力結果をメールまたはSlackで通知できるスクリプトをご紹介します。

本機能をご利用いただくことで、
広告配信の効果と天気条件の分析が可能になり、その結果をもとに入札調整を実施するなどさらに効果的な広告配信を実現できます。

おすすめのソリューション

本スクリプトをご利用いただき広告配信の効果分析を実施される場合は、その分析結果を活かした運用手法の1つとしてYahoo!天気・災害の天気予報データと連動させた広告配信もおすすめです。
ご参考:天気予報による地域ターゲティングの配信切替

▽出力されるレポートイメージ
パフォーマンスレポート(配信実績)とYahoo!天気・災害の天気予報データを連動させた結果のレポートイメージです。
詳細な見本は【出力レポート見本】のリンク先からご確認ください。


▽スクリプトの実行イメージ

詳細

【レポート概要】
1回のスクリプト実行で、Googleスプレッドシート(専用テンプレート)内の指定したシートに
Yahoo!天気・災害の天気予報データ」と「パフォーマンスレポート(配信実績)」を同時に出力します。
専用テンプレート上にあらかじめ設定された関数によって、出力した「天気予報データ」と「配信実績」を連動させたレポートを作成します。
出力するデータ 出力するデータの集計対象期間 データの粒度
Yahoo!天気・災害の
天気予報データ
スクリプトを実行した「当日0時~23時(=24時間分) 地域(都道府県)×時間( 0~23時)
パフォーマンスレポート
(配信実績)
スクリプトを実行した日の「昨日 指定したエンティティ単位
(キャンペーン/広告グループ/広告)

補足

・出力するデータの集計対象期間が、「天気予報データ(当日)」と「配信実績(昨日)」で異なるため、
 あらかじめ専用テンプレート上に設定された関数によって同じ日のデータ同士が連動するような仕組みになっています。
・詳細は【レポート出力結果の確認方法】をご確認ください


【出力できる項目】
レポート出力できる項目は、それぞれ以下リンク先からご確認ください。

■「Yahoo!天気・災害の天気予報データ」の出力項目:シート名「天気」のヘッダー項目
・「天気」シートには、ヘッダー項目の情報が全て出力されます(任意指定は不可)
・パフォーマンスレポート(配信実績)を出力するシート内で突合する項目は、必要な項目のみに削除可能です

■「パフォーマンスレポート(配信実績)」の出力項目:シート名「レポート項目」A列
・パフォーマンスレポート(配信実績)を出力するシート内で、ヘッダー項目を追加/削除することでA列にある項目を任意で選択可能です
 ※組み合わせ不可項目は広告管理ツールの仕様と同じです


【出力レポート見本】 レポート出力結果の見本は、それぞれ以下リンク先からご確認ください。

■「パフォーマンスレポート(配信実績)」と「天気予報データ」を突合した結果:シート名「見本_キャンペーンレポート」
 ※パフォーマンスレポート(配信実績)のエンティティをキャンペーンで指定した場合の見本です

■「Yahoo!天気・災害の天気予報データ」を出力した結果:シート名「見本_天気」

【レポート出力結果の確認方法】

スクリプトを実行すると、
パフォーマンレポート(配信実績)は昨日分、天気予報データは当日分を出力して
Googleスプレッドシートの専用テンプレート上の突合キー(関数)で
同じ日のデータ同士を連動させる仕様です。

そのため連動された結果を確認できるのはスクリプトを初回実行した翌日以降からです。

ご利用イメージ

目的 施策 / 利用スクリプト ご利用方法
通常で配信している広告の効果を気象条件で分析して、より効果的な配信手法を検討したい ■施策
通常の広告配信をしている期間中に、分析対象のキャンペーンを指定し、 スクリプトを2週間実行してレポート取得し、配信結果と気象条件を分析する
■利用スクリプト
天気予報レポート出力のスクリプト(本ページのスクリプト)
分析を開始したい日からスクリプトを実行してください。
例)
・分析を開始したい日:11/1
・スクリプト初回実行の日:11/1
天気予報連動配信を実施した結果を、天気予報データと照合しながら効果検証したい ■施策
・天気予報連動配信で「雨」の地域のみに広告配信する
・風速や気温など他の気象条件も広告効果に影響があったかについて配信結果を分析する

■利用スクリプト
1.天気予報レポート出力のスクリプト(本ページのスクリプト)
2.天気予報連動配信を実施するためのスクリプト
(例:ソリューション:天気予報による地域ターゲティングの配信切替
手順①
スクリプト1を、天気予報連動配信を開始する日より以前に実行開始してください。
(効果検証の前後比較データが不要の場合は、分析を開始したい日からの実行で問題ございません。)
手順②
スクリプト2を、天気予報連動配信の開始日から実行してください

例)通常配信のデータを前後比較用に14日間分取得しておく場合
・天気予報連動配信を開始したい日:11/1
・スクリプト1の初回実行の日:10/18
・スクリプト2の初回実行の日:11/1

ご利用の流れ

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

2.スプレッドシートのテンプレートを、ご自身のGoogle ドライブにコピーしてください。
 テンプレートをコピー
 コピーで作成したスプレッドシートのスプレッドシートIDを確認してメモしておいてください。スクリプト内の定数の設定で利用します。
 ※GoogleスプレッドシートIDの取得方法についてはこちらをご覧ください。

3. ご自身のGoogle ドライブにコピーしたテンプレートの項目を任意で変更してください。
 次章の「テンプレートの変更方法のご説明」を参考に、出力したい項目に合わせて変更してください。
 (項目を変更せずご利用いただくことも可能です)

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

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

ご注意

天気予報データは、過去の期間に遡って取得することができません。
必ず1日1回実行し、その日のデータを取得してください。

テンプレートの変更方法のご説明

「パフォーマンスレポート(配信実績)」の出力項目と、それと突合する「天気予報データ」の項目は、
Googleスプレッドシートの専用テンプレートの「パフォーマンスレポート(配信実績)」を出力するシートを変更することで、それぞれ任意で選択可能です。
■変更対象のシート
テンプレート初期値の場合:「キャンペーンレポート」シート、「広告グループレポート」シート、「広告レポート」シート
■変更不可の項目
項目 備考
A~C 列(日/時間/都道府県) 変更/削除/位置移動すべて不可
キー(関数) 変更/削除ともに不可
※ヘッダー項目が左から配信実績、キー、天気予報データとなる並びを順守すれば、
配信実績の項目の追加/変更/削除による列の移動は可能
■変更方法


「パフォーマンスレポート(配信実績)」の出力項目の変更について
変更箇所 テンプレート初期値の場合は、ヘッダー項目が青色の項目(D列~)
変更方法 ・ヘッダー項目を変更、または列を追加/削除することで自由に追加/変更/削除が可能です
・使用できる項目は「レポート項目」シートのA列を参照。(組み合わせ不可項目は、広告管理ツールの仕様と同じです)
・ヘッダー項目は、「レポート項目」シートのA列の項目をコピー&ペーストしてお使いください。
 項目名が異なる場合、レポートデータを取得できません。

突合する「天気予報データ」の項目の変更について
変更箇所 テンプレート初期値の場合は、ヘッダー項目が黄色の項目
変更方法 ・テンプレート初期値の場合は、出力可能な天気予報データの項目が全て用意されています
・不要な項目があれば、列ごと削除することで必要な項目だけに絞ることが可能です

ご注意

専用テンプレートの中の各シート※1にあらかじめ設定している突合キー※2の関数は1万行までです
レポートの取得期間により、必要に応じて1万行以降にも関数を手動でコピーしていただくようお願いします

※1 突合キーの設定箇所:各シート(シート名の初期値:キャンペーンレポート、広告グループレポート、広告レポート、天気)のヘッダー名「キー」の列
※2 突合キー:「パフォーマンスレポート(配信実績)」と「天気予報データ」を突合するための関数

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

後述のサンプルコードにおける各定数の設定方法についてご説明いたします。
設定が必要な定数 ・必ず設定する必要があります
設定が任意の定数 ・初期値でお使いいただけますが、任意の値に変更も可能です
・条件を変更したい場合など、必要に応じてご設定お願いします

設定が必要な定数

■スプレッドシートIDの指定例
次の例のように、' '(シングルクオーテーション)で囲んで指定してください。(スプレッドシートID:'12345abcde'の場合)
※GoogleスプレッドシートIDの取得方法についてはこちらをご覧ください。
const SPREAD_SHEET_ID = '12345abcde'; 

■シート名の指定例
【天気データを出力するシート名】
次の例のように、' '(シングルクオーテーション)で囲んで指定してください。((テンプレート初期値の場合:シート名は「天気」)
const SHEET_NAME_WEATHER = '天気';

【レポートデータを出力するシート名】
出力するパフォーマンスレポート(配信実績)のエンティティ(データの粒度)は、キャンペーン、広告グループ、広告の中から選択が可能です。
レポートを出力したいエンティティに対応するシートを選択し、次の例のように、' '(シングルクオーテーション)で囲んで指定してください。

・キャンペーンレポートを出力する場合(テンプレート初期値の場合:シート名は「キャンペーンレポート」)
const SHEET_NAME_REPORT = 'キャンペーンレポート';
・広告グループレポートを出力する場合(テンプレート初期値の場合:シート名は「広告グループレポート」)
const SHEET_NAME_REPORT = '広告グループレポート';
・広告レポートを出力する場合(テンプレート初期値の場合:シート名は「広告レポート」)
const SHEET_NAME_REPORT = '広告レポート';

補足

・専用テンプレートの各シートのヘッダー項目をもとに、レポートの出力対象となるエンティティ(データの粒度)を判別します。
 そのため、こちらの定数でシートを指定することで、出力するレポートのエンティティを指定する形となります。

・レポートの出力対象を特定のキャンペーンIDや広告グループIDで絞りたい場合は、設定が任意の定数で指定してください。
・シートの複数指定は不可です。
 異なるエンティティで出力したい場合は、「定数を変更して再実行する」または「出力したいエンティティの数だけスクリプトを設定」してください。

設定が任意の定数定数

キャンペーンID、広告グループIDを指定する例 ※出力対象を絞り込む場合
ャンペーン、広告グループについては、それぞれ全指定またはIDで指定可能です。

【キャンペーンIDを指定する場合】
・次の例のように、' '(シングルクオーテーション)で囲み、半角カンマ区切りで入力してください。
const TARGET_CAMPAIGN_ID = ['123456','234567'];
・次の例のように、空欄の場合は全てのキャンペーンを対象として出力します。
const TARGET_CAMPAIGN_ID = [];
【広告グループIDを指定する場合】
・次の例のように、' '(シングルクオーテーション)で囲み、半角カンマ区切りで入力してください。
const TARGET_ADGROUP_ID = ['1234567','2345678'];
・次の例のように、空欄の場合は全てのキャンペーンを対象として出力します。
const TARGET_ADGROUP_ID = [];

ご注意

出力対象のデータ量が多い場合、スクリプト実行後にタイムアウトエラーになる可能性がございます。
その場合は以下の方法で出力対象を絞っていただくことを推奨いたします。
・キャンペーンIDか広告グループIDで出力対象を指定する
・上位のエンティティを指定する
 例)「広告グループレポート」→「キャンペーンレポート」

■レポート出力対象とする地域(都道府県)の指定例

天気予報レポートの出力対象とする地域は、初期値で47都道府県としています。
配信対象"外"としたい地域がある場合は、その都道府県を初期値から削除してください。

(初期値:47都道府県を指定する場合 )
const TARGET_AREA = ["北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県",
  "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県",
  "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県",
  "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県",
  "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県",
  "大分県", "宮崎県", "鹿児島県", "沖縄県"];//47都道府県
※地域ターゲティングの設定で配信地域を特定の地域にしている場合は、
 定数が初期値(47都道府県)でも地域ターゲティングで指定している都道府県のみがレポート出力されます

(関東のみを指定する場合)
const TARGET_AREA = ["茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県"];

■メール、Slackの指定例

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

サンプルコード

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

/*このソースコードは MIT License のもとで提供されています。
https://ads-developers.yahoo.co.jp/ja/ads-script/post/30418913.html
■スクリプト内容
指定したエンティティ単位の昨日分のレポートと、本日の天気予報をスプレッドシートに出力します。
■利用方法
1.テンプレートスプレッドシートをお使いのGoogleアカウントにコピーしてください。
2.当スクリプトを、ディスプレイ広告のアカウントに設定してください。
3.定数を以下のように設定してください。
4.実行頻度を1日1回(時間は任意)に設定してください。
5.必要に応じて、不要な列を削除してください。
■定数
・SPREAD_SHEET_ID   //スプレットシートIDを指定
・SHEET_NAME_WEATHER//天気データを出力するシート名
・SHEET_NAME_REPORT //レポートデータを出力するシート名(実際にご覧いただくシートです)
・TARGET_CAMPAIGN_ID   //出力対象キャンペーンID。空欄の場合、全て出力します。
                        指定する際は、シングルクオーテーションで囲み、半角カンマ区切りで入力してください。
                         例)const TARGET_CAMPAIGN_ID = ['123456','234567'];
・TARGET_ADGROUP_ID   //出力対象広告グループID。空欄の場合、全て出力します。
                        指定する際は、シングルクオーテーションで囲み、半角カンマ区切りで入力してください。
                         例)const TARGET_ADGROUP_ID = ['1234567','2345678'];
・TARGET_AREA       //対象エリア(初期値は47都道府県)
・FLAG_MAIL         //結果をメール送信するならtrue、しないならfalse
・MAIL_TO           //メール送信先のYahoo! BusinessID
・MAIL_TITLE        //メールタイトル
・FLAG_SLACK        //結果をSlack送信するならtrue、しないならfalse
・URL_FETCH_APP     //SlackのWebhook URL
■制限事項
・データ量によっては、タイムアウトになることがございます。その場合、以下のどちらかでご対応ください。
  1.キャンペーンIDフィルタか、広告グループIDフィルタを指定する
    例)キャンペーンIDフィルタを追加する場合
     const TARGET_CAMPAIGN_ID = ['123456','234567'];
  2.上位のエンティティのシートを指定する
    例)「広告グループレポート」→「キャンペーンレポート」
*/
//設定が必要な定数
const SPREAD_SHEET_ID = '';
const SHEET_NAME_WEATHER = '天気';
const SHEET_NAME_REPORT = 'キャンペーンレポート';
//設定が任意の定数(初期値でお使いいただけますが、任意の値に変更も可能です)
const TARGET_CAMPAIGN_ID = [];
const TARGET_ADGROUP_ID = [];
const TARGET_AREA = ["北海道", "青森県", "岩手県", "宮城県", "秋田県", "山形県", "福島県",
  "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "新潟県", "富山県",
  "石川県", "福井県", "山梨県", "長野県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県",
  "京都府", "大阪府", "兵庫県", "奈良県", "和歌山県", "鳥取県", "島根県", "岡山県", "広島県",
  "山口県", "徳島県", "香川県", "愛媛県", "高知県", "福岡県", "佐賀県", "長崎県", "熊本県",
  "大分県", "宮崎県", "鹿児島県", "沖縄県"];//47都道府県
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';
//設定不要な定数(変更するとエラーになります)
let TEXT_MESSAGE_ARRAY = [];
const accountId = AdsUtilities.getCurrentAccountId();
const SS_URL = 'https://docs.google.com/spreadsheets/d/' + SPREAD_SHEET_ID;
function main() {
  try {
    const ss = validateAndGetSpreadsheet();
    outputWeather(ss);
    outputReport(ss);
    logAndMessage('スプレッドシートへのレポート・天気予報データの出力が完了しました。\n' + SS_URL);
    sendMailAndSlack();
  } catch (error) {
    MAIL_TITLE = '!!エラーが発生しました!!' + MAIL_TITLE;
    logAndMessage('エラーが発生しました!!詳細は管理画面をご確認ください。\n' + error);
    sendMailAndSlack();
    throw error;
  }
}
//天気をスプレッドシートに出力
function outputWeather(ss) {
  const sh = validateAndGetSheet(ss, SHEET_NAME_WEATHER);
  const todayStr = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd');
  let outputData = [];
  for (let i = 0; i < TARGET_AREA.length; i++) {
    const areaName = TARGET_AREA[i];
    const weather = WeatherApp.getWeatherByName(
      areaName,
      'TODAY'
    );
    //1日単位データ
    const dayWeather = weather[0].day[0];
    //1時間単位データごとに行生成
    for (let j = 0; j < weather[0].hour.length; j++) {
      const hourWeather = weather[0].hour[j];
      outputData.push([
        todayStr + ':' + j + ':' + areaName,
        areaName,
        todayStr,
        j,
        hourWeather.weather.telop,//天気テロップ(1h)
        hourWeather.precip.value + hourWeather.precip.unit,//降水量(1h)
        hourWeather.temp,//気温
        hourWeather.probPrecip + '%',//降水確率(1h)
        hourWeather.humidity + '%',//湿度(1h)
        hourWeather.snowFall + 'cm',//降雪量(1h)
        hourWeather.windDirection.name,//風向き(1h)
        hourWeather.windSpeed.value + hourWeather.windSpeed.unit,//風速(1h)
        dayWeather.temp.min,//最低気温(1日)
        dayWeather.temp.minDiff,//最低気温前日差(1日)
        dayWeather.temp.max,//最高気温(1日)
        dayWeather.temp.maxDiff,//最高気温前日差(1日)
        dayWeather.wave,//波予報(1日)
        dayWeather.sunrise,//日の出時間(1日)
        dayWeather.sunset//日の入時間(1日)
      ]);
    }
  };
  const newRow = sh.getLastRow() + 1;
  sh.getRange('A' + newRow).setValues(outputData);
}
//レポートをスプレッドシートに出力
function outputReport(ss) {
  const sh = validateAndGetSheet(ss, SHEET_NAME_REPORT);
  const entityFields = getEntityFields(sh);
  const operand = {
    accountId: AdsUtilities.getCurrentAccountId(),
    fields: entityFields,
    reportDateRangeType: 'YESTERDAY'
  };
  // フィルタを追加
  const filters = [];
  if (TARGET_CAMPAIGN_ID.length > 0) {
    filters.push({
      field: 'CAMPAIGN_ID',
      filterOperator: 'IN',
      values: TARGET_CAMPAIGN_ID
    });
  }
  if (TARGET_ADGROUP_ID.length > 0) {
    filters.push({
      field: 'ADGROUP_ID',
      filterOperator: 'IN',
      values: TARGET_ADGROUP_ID
    });
  }
  // フィルタがある場合のみ設定
  if (filters.length > 0) {
    operand.filters = filters;
  }
  const newRow = getLastRow(sh) + 1;
  Logger.log('レポート条件:' + JSON.stringify(operand));//何かあった時用に出力しておく
  const reportData = AdsUtilities.getDisplayReport(operand).reports[0].rows;
  if (reportData.length > 0) {
    sh.getRange('A' + newRow).setValues(reportData);
  } else {
    Logger.log('レポートデータはありませんでした');
  }
}
//Range.getLastRow()がYASにはないので(Sheetならある)
function getLastRow(sh) {
  const ssAcolData = sh.getRange('A:A').getValues();
  //空白も値が入っている扱いになる
  let lastRowNo = 1;
  for (let i = 1; i < ssAcolData.length; i++) {//ヘッダは抜く
    const row = ssAcolData[i];
    if (row == '') {
      lastRowNo = i;//1つ前が値の入っている最後の行なので、-1は不要
      break;
    }
  }
  return lastRowNo;
}
//スプレッドシートデータからリクエスト用operand生成しつつ組み合わせ不可のチェック
function getEntityFields(sh) {
  const ssReportData = sh.getDataRange().getValues();
  const ssHeaderRow = ssReportData[0];//スプレッドシート側のヘッダ行を取得
  if (ssHeaderRow.length < 2) {//ヘッダが空の場合も、空文字で要素1の配列になるので
    throw new Error('スプレッドシートはテンプレートをご利用ください');
  }
  //マスタ情報取得
  const { fieldConvMap, impossibleCombiFieldMap } = getReportFieldMasterMaps();
  //スプレッドシートデータからリクエスト用operand生成しつつ組み合わせ不可のチェック
  let operandFields = [];
  for (let i = 0; i < ssHeaderRow.length; i++) {
    const ssFieldName = ssHeaderRow[i];
    if (ssFieldName == 'キー') {//キー以降は不要
      break;
    }
    if (!fieldConvMap.has(ssFieldName)) {
      throw new Error('スプレッドシートのヘッダ名は、規定のレポート項目名を指定してください:' + ssFieldName);
    }
    const convFieldName = fieldConvMap.get(ssFieldName);
    const impossibleCombiArr = impossibleCombiFieldMap.get(convFieldName);
    //組み合わせ不可チェック
    if (impossibleCombiArr != null) {
      for (let j = 0; j < impossibleCombiArr.length; j++) {
        if (operandFields.includes(impossibleCombiArr[j])) {
          throw new Error('組み合わせ不可な項目です。' + convFieldName + 'と' + impossibleCombiArr[j]);
        }
      }
    }
    //全部通ったらOK
    operandFields.push(convFieldName);
  }
  return operandFields;
}
//以下の2つのMapを生成する
//fieldConvMap...{日本語名:リクエスト用項目名を作成}
//impossibleCombiFieldMap...{日本語名:NG項目の配列}
function getReportFieldMasterMaps() {
  const reportFieldMaster = Display.ReportDefinitionService.getReportFields({
    reportType: 'AD'
  }).rval;
  let fieldConvMap = new Map();
  let impossibleCombiFieldMap = new Map();
  for (let i = 0; i < reportFieldMaster.fields.length; i++) {
    const field = reportFieldMaster.fields[i];
    fieldConvMap.set(field.displayFieldNameJa, field.fieldName);
    impossibleCombiFieldMap.set(field.fieldName, field.impossibleCombinationFields);
  }
  return { fieldConvMap: fieldConvMap, impossibleCombiFieldMap: impossibleCombiFieldMap };
}
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, sheetName) {
  if (sheetName === '') {
    throw new Error('シート名を設定してください。');
  }
  const sh = ss.getSheetByName(sheetName);
  if (sh === null) {
    throw new Error('シートが開けませんでした。シート名を確認してください。');
  }
  return sh;
}
//ログ、メールなどに流すテキスト
function logAndMessage(text) {
  Logger.log(text);
  TEXT_MESSAGE_ARRAY.push(text);
}
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: 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スプレッドシートに結果が出力されていれば成功です。