【GAS】Yahoo ディスプレイ広告の運用データを自働でスプレッドシートに取得する方法(v9対応)

2022年12月11日

Yahoo ディスプレイ広告のインプレッションやコンバージョン数などの運用データをスプレッドシートに自働で取得する方法です。今回はGAS(GoogleAppsScript)を使用します。

他の広告媒体はこちら

Yahoo広告アプリケーションの登録

Yahoo!広告 API管理ツールにアクセスして、広告運用を行なっているYahoo!ビジネスIDでログインします。

アプリケーションを登録します。

登録画面で必要事項を入力して確認を選択します。リダイレクトURIには「oob 」を入力してください。

登録が完了したら下記のようにアプリケーションの情報が表示されるため、クライアントIDクライアントシークレットをメモします。

認証コードの取得

下記のURLを作成します。
・CLIENT_ID:冒頭でメモしたクライアントID
・REDIRECT_URI:アプリケーションの登録で設定したURL
・THIS_VALUE_SHOULD_BE_UNIQUE_PER_REQUEST:任意の文字列(※乱数でOK)

https://biz-oauth.yahoo.co.jp/oauth/v1/authorize?response_type=code
&client_id=CLIENT_ID
&redirect_uri=REDIRECT_URI
&scope=yahooads
&state=THIS_VALUE_SHOULD_BE_UNIQUE_PER_REQUEST

ブラウザで上記にアクセスし、アクセス許可の承認をすると認可コードが表示されます。

https://developer.yahoo.co.jp/yconnect/v1/server_app/explicit/authorization.html

アクセストークン・リフレッシュトークンの取得

ターミナルなどから下記のリクエストを実行します。
・CLIENT_ID:冒頭でメモしたクライアントID
・CLIENT_SECRET:冒頭でメモしたクライアントシークレット
・REDIRECT_URI:アプリケーションの登録で設定したURL
・AUTH_CODE:先ほど取得した認可コード

curl -X GET \
https://biz-oauth.yahoo.co.jp/oauth/v1/token?grant_type=authorization_code
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&redirect_uri=REDIRECT_URI
&code=AUTH_CODE

下記のようにアクセストークンとリフレッシュトークンが返却されるので、それぞれの値をメモします。

なお、ブラウザでもアクセス可能です。

アクセストークンは1時間で失効するため、リフレッシュトークンを使って都度で新しいアクセストークン を発行する必要があります。

スプレッドシートとGASの作成

スプレッドシートを作成し、「db」シートを作成してください。

「ツール」→「スクリプト」からスクリプトを作成します。コードは下記のとおりです。

function main() {
    // シート定義
    const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('db');
 

    // 各種設定
    const clientId = "****"; // クライアントID
    const clientSecret = "****"; // クライアントシークレット
    const refreshToken = "****"; // リフレッシュトークン
    const accountId = "****"; // 広告アカウントID


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

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

  reportJobIds.forEach(reportJobId => {
    // レポートの作成が完了するまでループ
    while (!yahooAdcheckDisplayReport(accessToken, accountId, reportJobId)) {
      // レポート作成完了まで10秒間待つ
      Utilities.sleep(10000);
    }
    // レポートの取得
    const downloadResponse = yahooAdDownloadDisplayReport(accessToken, accountId, reportJobId);

    let target_row = ss.getRange(ss.getMaxRows(), 2).getNextDataCell(SpreadsheetApp.Direction.UP).getRow() + 1;
    ss.getRange(target_row, 1, downloadResponse.length, downloadResponse[0].length).setValues(downloadResponse)

  });
}

// アクセストークン取得
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 yahooAdCreateDisplayReport(accessToken, accountId) {
  const url = "https://ads-display.yahooapis.jp/api/v9/ReportDefinitionService/add";
  const httpResponse = UrlFetchApp.fetch(url, {
    method: 'post',
    // muteHttpExceptions : true,
    contentType: 'application/json',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
    payload: JSON.stringify({
      accountId: accountId,
      operand: [
        {
          fields: [
            "ACCOUNT_ID",
            "ACCOUNT_NAME",
            "CAMPAIGN_ID",
            "CAMPAIGN_NAME",
            // "ADGROUP_ID",
            // "ADGROUP_NAME",
            // "AD_ID",
            // "AD_NAME",
            // "AD_TYPE",
            // "URL_ID",
            // "URL_NAME",
            // "PREF_ID",
            // "PREF_NAME",
            // "CITY_ID",
            // "CITY_NAME",
            // "WARD_ID",
            // "WARD_NAME",
            // "GENDER",
            // "AGE",
            // "MONTH",
            "DAY",
            "CLICK", // クリック数
            "CONVERSIONS", // コンバージョン数
            "COST", // 費用
            "IMPS", // 表示回数
          ],
          reportCompressType: "NONE", // レポートファイルは圧縮しない
          // reportDateRangeType: "YESTERDAY",

          // 期間指定するときは"CUSTOM_DATE"にする
          reportDateRangeType: "CUSTOM_DATE",
          dateRange: {
            startDate: "20211101",
            endDate: "20211130",
          }, 
          reportDownloadEncode: "UTF8",
          reportDownloadFormat: "CSV",
          reportLanguage: "JA",
        }
      ],
    }),
  });
  const status = httpResponse.getResponseCode();
  if (status !== 200) {
    throw 'HttpRequestError';
  }
  const response = JSON.parse(httpResponse.getContentText());
  return response;
}



// 広告レポート(CSV形式)取得APIをコールし、結果をパースする関数
function yahooAdDownloadDisplayReport(accessToken, accountId, reportJobId) {
  const url = "https://ads-display.yahooapis.jp/api/v9/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();
  let lines = response.split('\n');
  // 対象期間に費用の発生した広告グループが存在しない場合、空配列を返す
  if (lines.length === 1) {
    return [];
  }

  // 最後の行:空行 のため、sliceで除外する
  lines = lines.slice(0, -1).map(elm => elm.split(","))
  return lines
  
}

// レポートの作成状況を確認する
function yahooAdcheckDisplayReport(accessToken, accountId, reportJobId) {
  const url = "https://ads-display.yahooapis.jp/api/v9/ReportDefinitionService/get";
  const httpResponse = UrlFetchApp.fetch(url, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
    },
    payload: JSON.stringify({
      accountId,
      "reportJobIds": [reportJobId],
    }),
  });
  const status = httpResponse.getResponseCode();
  if (status !== 200) {
    throw 'HttpRequestError';
  }
  const json = JSON.parse(httpResponse)
  console.log(json.rval.values[0].reportDefinition)

  if (json.rval.values[0].reportDefinition.reportJobStatus == "COMPLETED") {
    return true
  } else if (json.rval.values[0].reportDefinition.reportJobStatus == "IN_PROGRESS") {
    return false
  } else {
    throw 'HttpRequestError';
  }

}

参考

Yahoo!マーケティング

https://github.com/yahoojp-marketing/ads-display-api-documents/blob/master/bestpractice/ja/display_ads_report.md

APIリファレンス

https://ads-developers.yahoo.co.jp/reference/ads-display-api/v9/ReportDefinitionService/

GAS,広告運用

Posted by Next-k