GASでYahoo広告の週次レポートを自動化するslackbotを作ってみた

# 何を作ったか

Yahoo広告のレポート作成を自動で行い、スラックチャンネルに投稿するGASスクリプトを作りました。

サンプルのコードでは、広告グループごとに費用、表示回数、クリック数、コンバージョン数を集計し、週次で以下のような自動レポートをスラックチャンネルに自動投稿します。

```
【Yahoo検索広告】広告グループ〇〇の週報です。
先週(月-日)の実績→費用：14074円、表示回数：23926回、クリック数：196回、コンバージョン数：2
```

### 前提となる知識

* Yahooリスティング広告
* Google Apps Scriptの使用方法
* Slack Webhook URLの設定方法

## API利用開始までの流れ

### Yahoo広告APIの利用申請

[こちらのページ](https://ads-developers.yahoo.co.jp/developercenter/ja/startup-guide/apply-api-use.html)から、利用を申し込む必要があります。
利用申請を出してから許可が降りるまで、数日かかるとの記載がありましたが、私の場合は数分で利用許可のメールが届きました。

### アプリケーションの登録

[こちらのページ](https://ads-developers.yahoo.co.jp/developercenter/ja/startup-guide/app-registration.html)の手順に従い、アプリケーションを登録します。 
クライアントIDとクライアントシークレットを控えておきます。

### 初回アクセストークンとリフレッシュトークンの取得

[こちらのページ](https://ads-developers.yahoo.co.jp/developercenter/ja/startup-guide/api-call.html)の手順に従い、トークンを取得します。
アクセストークンの有効期限は１時間です。リフレッシュトークンは、アプリケーションの承認を解除するまで有効です。


## ソースコード

Yahoo広告APIから、コスト、インプレッション、クリック数、コンバージョン数などの値を取得するには、レポート作成APIを叩いた後、生成されたレポートをダウンロードする必要があります。

レポートの生成は非同期処理となり、少し時間がかかります。本スクリプトでは10秒のsleep処理を入れて、レポート生成完了を待っています。

### 全体の処理

メインの関数です。最終的には、この関数をGASのトリガーに設定します。

```javascript
function main() {
  // 各種設定
  const clientId = "YAHOO_AD_API_CLIENT_ID" // クライアントID
  const clientSecret = "YAHOO_AD_API_CLIENT_SECRET"; // クライアントシークレット
  const refreshToken = "YAHOO_AD_API_REFRESH_TOKEN"; // リフレッシュトークン
  const accountId = "YAHOO_AD_ACCOUNT_ID"; // 広告アカウントID
  const slackUrl = "SLACK_NOTIFICATION_URL"; // slack webhook url

  // アクセストークン取得
  const loginResponse = yahooAdLogin(clientId, clientSecret, refreshToken);
  const accessToken = loginResponse.access_token;

  // YAHOO広告レポートを作成し、作成ジョブのIDを取得
  const reportName = Moment.moment().format('YYYYMMDD_HHmmss');
  const createReportResponse = yahooAdCreateReport(accessToken, accountId, reportName);
  const reportJobIds = createReportResponse.rval.values.map(item => Number(item.reportDefinition.reportJobId));

  // レポート作成完了まで10秒間待つ
  Utilities.sleep(10000);

  reportJobIds.forEach(reportJobId => {
    // レポートの取得
    const downloadResponse = yahooAdDownloadReport(accessToken, accountId, reportJobId);

    // 広告グループごとのレポートをスラックに通知
    downloadResponse.forEach(report => {
      const message1 = `【Yahoo検索広告】${report.adGroupName}の週報です。`
      const message2 = `先週(月-日)の実績→費用：${report.cost}円、表示回数：${report.impressions}回、クリック数：${report.clicks}回、コンバージョン数：${report.conversions}`;
      UrlFetchApp.fetch(slackUrl, {
        method: 'post',
        contentType: 'application/json',
        payload: JSON.stringify({ text: message1 + message2 }),
         muteHttpExceptions: true,
      });
    });
  });
}

```



### Yahoo広告APIのコール部

アクセストークンの取得APIに加え、[レポート作成API](https://ads-developers.yahoo.co.jp/reference/ads-search-api/v3/ReportDefinitionService/add/) と [レポートダウンロードAPI](https://ads-developers.yahoo.co.jp/reference/ads-search-api/v3/ReportDefinitionService/download/) の2つのYahoo広告APIを利用します。


```javascript
// アクセストークン取得
function yahooAdLogin(clientId, clientSecret, refreshToken) {
  const url = "https://biz-oauth.yahoo.co.jp/oauth/v1/token";
  const httpResponse = UrlFetchApp.fetch(url, {
    method: 'get',
    payload: {
      grant_type: "refresh_token",
      client_id: clientId,
      client_secret: clientSecret,
      refresh_token: refreshToken,
    },
  });
  const status = httpResponse.getResponseCode();
  if (status !== 200) {
    throw 'HttpRequestError';
  }
  const response = JSON.parse(httpResponse.getContentText());
  return response;
}

// 広告レポートを作成するAPIのコール
function yahooAdCreateReport(accessToken, accountId, reportName) {
  const url = "https://ads-search.yahooapis.jp/api/v3/ReportDefinitionService/add";
  const httpResponse = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
    payload: JSON.stringify({
      accountId,
      operand: [
        {
          fields: [
            "ADGROUP_ID", // 広告グループID
            "ADGROUP_NAME", // 広告グループ名
            "COST", // 費用
            "IMPS", // 表示回数
            "CLICKS", // クリック数
            "CLICK_RATE", // クリック率
            "AVG_CPC", // 平均クリック単価 
            "CONVERSIONS", // コンバージョン数
            "CONV_RATE", // コンバージョン率
            "CONV_VALUE", // コンバージョン値
          ],
          reportCompressType: "NONE", // レポートファイルは圧縮しない
          reportDateRangeType: "LAST_7_DAYS", // 過去7日間(当日含まない)の期間指定
          reportDownloadEncode: "UTF-8",
          reportDownloadFormat: "CSV",
          reportIncludeDeleted: "TRUE",
          reportIncludeZeroImpressions: "FALSE",
          reportLanguage: "JA",
          reportName,
          reportType: "ADGROUP" // 広告グループごとに集計したレポートを出力する
        }
      ],
    }),
  });
  const status = httpResponse.getResponseCode();
  if (status !== 200) {
    throw 'HttpRequestError';
  }
  const response = JSON.parse(httpResponse.getContentText());
  return response;
}


// 広告レポート(CSV形式)取得APIをコールし、結果をパースする関数
function yahooAdDownloadReport(accessToken, accountId, reportJobId) {
  const url = "https://ads-search.yahooapis.jp/api/v3/ReportDefinitionService/download";
  const httpResponse = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
    payload: JSON.stringify({
      accountId,
      reportJobId,
    }),
  });
  const status = httpResponse.getResponseCode();
  if (status !== 200) {
    throw 'HttpRequestError';
  }
  const response = httpResponse.getContentText();
  const lines = response.split('\n');
  // 対象期間に費用の発生した広告グループが存在しない場合、空配列を返す
  if (lines.length === 3) {
    return [];
  }
  // 最初の行:ヘッダー行、最後から２番目の行:合計の行、最後の行:空行 のため、sliceで除外する
  return lines.slice(1, -2).map((line) => {
    const data = line.split(',');
    return {
      adGroupId: String(data[0]),
      adGroupName: String(data[1]),
      cost: Number(data[2]),
      impressions: Number(data[3]),
      clicks: Number(data[4]),
      clickRate: Number(data[5]),
      cpc: Number(data[6]),
      conversions: Number(data[7]),
      conversionRate: Number(data[8]),
      conversionValue: Number(data[9]),
    };
  });
}
```

# まとめ

Web広告の運用では、タイムリなーレポートの作成を要求されます。
これらのレポーティング作業をマニュアルで行うのは非常に骨が折れますね。
APIを活用してレポーティングを自動化し、作業時間を短縮していきたいですね。