Facebook広告APIによるKPI計測の自動化

# 対象
- Facebook広告([Facebook広告マネージャ](https://www.facebook.com/ads/manager))を利用し、KPIを計測している
- Googleスプレッドシートに、KPIを**手入力**もしくは広告マネージャからエキスポートしたデータを**コピペ**している

# 目標
- デイリーで、GoogleスプレッドシートにKPIを自動的に計測する

### 前提
- 前日（00:00～23:59）のKPIが対象
- 1日1度のみ自動的に計測（例：出社前の時間など）
- KPI取得のため利用するFacebook広告APIのバージョンは現時点での最新であるv2.8とする

# おおまかな手順
1. [Facebookアクセストークンの取得](#facebook%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AE%E5%8F%96%E5%BE%97)
2. [Facebook広告キャンペーンIDの確認](#facebook%E5%BA%83%E5%91%8A%E3%82%AD%E3%83%A3%E3%83%B3%E3%83%9A%E3%83%BC%E3%83%B3id%E3%81%AE%E7%A2%BA%E8%AA%8D)
3. [GAS(Google Apps Script)とFacebook広告APIを利用して、Facebook広告KPIをサマリーシートに出力](#gasgoogle-apps-script%E3%81%A8facebook%E5%BA%83%E5%91%8Aapi%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6facebook%E5%BA%83%E5%91%8Akpi%E3%82%92%E3%82%B5%E3%83%9E%E3%83%AA%E3%83%BC%E3%82%B7%E3%83%BC%E3%83%88%E3%81%AB%E5%87%BA%E5%8A%9B)
4. [GASの実行をスケジュール化](#gas%E3%81%AE%E5%AE%9F%E8%A1%8C%E3%82%92%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%8C%96)

## Facebookアクセストークンの取得
1. Facebookアクセストークンを取得します。Facebookアクセストークン取得手順の詳細は、[Facebook公式サイト](https://developers.facebook.com/docs/marketing-api/access)を参照ください。私は[こちら](https://wayohoo.com/facebook/tips/how-to-get-an-access-token-of-the-facebook-api.html)の日本語サイトがわかりやすかったので参考にさせていただきました。  
なお指定するパーミッション（scope）は、**ads_management**を指定します。パーミッション一覧は[こちら](https://developers.facebook.com/docs/facebook-login/permissions)を参考ください。
2. 「Facebook_Access_Token」Googleスプレッドシートを作成します。カラムは以下の通りです。

| | A | B |
|:-----------------|:-----------------|:-----------------|
| 1 | Access Token | |
| 2 | 取得したFacebookアクセストークン | |
| ... | ... | |

なおアクセストークンには有効期限があるため定期的な更新が必要です。有効期限の確認は、[Facebook公式のデバッグツール](https://developers.facebook.com/tools/debug/accesstoken/)から確認可能です。


## Facebook広告キャンペーンIDの確認
1. [Facebook広告マネージャ](https://www.facebook.com/ads/manager)にログインします。
2. 「メニュー→パワーエディタ」を選択します。
![sc_facebook_manager.png](https://qiita-image-store.s3.amazonaws.com/0/128800/1b308b5a-7dcf-6344-1dcc-35856ecddf29.png)


3. ページ右側の鉛筆アイコンを選択します。
4. キャンペーンIDを確認することができます。そのIDをメモします。
![sc_facebook_manager_cpid.png](https://qiita-image-store.s3.amazonaws.com/0/128800/f30d3e82-70c1-922d-7a79-f0ea517ab4da.png)


## GAS(Google Apps Script)とFacebook広告APIを利用して、Facebook広告KPIをサマリーシートに出力
「Daily_Facebook_Like_KPI_2017」、「Daily_Facebook_Install_KPI_2017」Googleスプレッドシートを作成します。カラムは以下の通りです。

Daily_Facebook_Like_KPI_2017シート

| | A | B | C | E | E | F | G | H | I | J | K | L | M |
|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|
| 1 | Facebook Page Like KPI | | | | | | | | | | | | |
| 2 | Date | date_start | date_stop | campaign_id | campaign_name | like | reach | spend | clicks | unique_clicks | ctr | cpm | Cost / Results |
| 3 | 2017/01/01 | | | | | | | | | | | | =H3/F3 |
| 4 | 2017/01/02 | | | | | | | | | | | | =H4/F4 |
| 5 | 2017/01/03 | | | | | | | | | | | | =H5/F5 |
| ... | ... | | | | | | | | | | | | |
| 367 | 2017/12/31 | | | | | | | | | | | | =H367/F367 |

Daily_Facebook_Install_KPI_2017シート

| | A | B | C | E | E | F | G | H | I | J | K | L | M |
|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|:-----------------|
| 1 | Facebook App Install KPI | | | | | | | | | | | | |
| 2 | Date | date_start | date_stop | campaign_id | campaign_name | install | reach | spend | clicks | unique_clicks | ctr | cpm | Cost / Results |
| 3 | 2017/01/01 | | | | | | | | | | | | =H3/F3 |
| 4 | 2017/01/02 | | | | | | | | | | | | =H4/F4 |
| 5 | 2017/01/03 | | | | | | | | | | | | =H5/F5 |
| ... | ... | | | | | | | | | | | | |
| 367 | 2017/12/31 | | | | | | | | | | | | =H367/F367 |

次にGASを作成します。
メニュー「ツール＞スクリプト エディタ...」を選択します。

以下、サマリーシートに、KPIを出力するためのGASコードです。なお具体的なGASの記法などは[リファレンス](https://developers.google.com/apps-script/guides/sheets)やググれば色々参考となるサイトが見つかるので、そちらを参照ください。

```gas:Code.gs
// Facebook Access Token
var ACCESS_TOKEN = "";

function getReportByHttpRequest(today, fields, campaign_id){
  var payload =
      {
        "time_range" : "{'since':'" + today + "','until':'" + today + "'}",
        "fields" : fields,
        "access_token" : ACCESS_TOKEN
      };
  
  var options =
      {
        "method" : "POST",
        "payload" : payload
      };
  
  return UrlFetchApp.fetch("https://graph.facebook.com/v2.8/" + campaign_id + "/insights", options);
}

function getProgressByHttpRequest(report_run_id){
  var url = "https://graph.facebook.com/v2.8/" + report_run_id + "?access_token=" + ACCESS_TOKEN;
  var response = UrlFetchApp.fetch(url);
  var json = JSON.parse(response);
  return json["async_percent_completion"];
}

function getFacebookReport(response) {
  var json = JSON.parse(response);
  var report_run_id = json["report_run_id"];
  Logger.log(report_run_id);

  // 広告APIの取得結果は非同期なため進行状況を定期的に確認する
  var progress_percentage = getProgressByHttpRequest(report_run_id);
  Logger.log(progress_percentage);
  // 進行状況が100%未満の場合は、10秒スリープし、再度進行状況を確認する
  while (progress_percentage < 100) {
    Utilities.sleep(10000);
    progress_percentage = getProgressByHttpRequest(report_run_id);
    Logger.log(progress_percentage);
  }
  
  var url = "https://graph.facebook.com/v2.8/" + report_run_id + "/insights?access_token=" + ACCESS_TOKEN;
  var response = UrlFetchApp.fetch(url);
  json = JSON.parse(response);
  return json;
}

function kpiFunction() {
  // キャンペーンID
  var APP_INSTALL_CAMPAIGN_ID = "";
  var PAGE_LIKE_CAMPAIGN_ID = "";
  // シート参照
  var sheet_Daily_facebook_install_KPI_2017 = SpreadsheetApp.getActive().getSheetByName('Daily_Facebook_Install_KPI_2017');
  var sheet_Daily_facebook_like_KPI_2017 = SpreadsheetApp.getActive().getSheetByName('Daily_Facebook_Like_KPI_2017');
  var sheet_Facebook_Access_Token = SpreadsheetApp.getActive().getSheetByName('Facebook_Access_Token');
  // シート上のFacebookアクセストークンと日付を取得
  ACCESS_TOKEN = sheet_Facebook_Access_Token.getRange("A2").getValue();
  var value_total_date = sheet_Daily_facebook_install_KPI_2017.getRange(3, 1, 365, 1).getValues();// セルA3, A368取得
  // 昨日の日付取得
  var yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  yesterday = Utilities.formatDate(new Date(yesterday), "JST", "yyyy-MM-dd");
  
  // サマリーデータの出力対象となる行を取得
  var target_row = 0;
  var date;
  for(i = 0; i < 365; i++){
    date = Utilities.formatDate(new Date(value_total_date[i]), "JST", "yyyy/MM/dd");
    if(yesterday == date){// KPI計測対象日は昨日の日付
      // KPIデータ出力対象となる行：+1(ヘッダ分-1:昨日の日付)
      target_row = i + 1;
      break;
    }
  }
  
  // Facebook App Install広告のレポート取得
  var response_install = getReportByHttpRequest(yesterday, "date_start,date_stop,campaign_id,campaign_name,reach,spend,clicks,unique_clicks,ctr,cpm,actions", APP_INSTALL_CAMPAIGN_ID);
  if(response_install.getResponseCode() != 200) {
    Logger.log("Facebook App Install広告レポートを取得できませんでした");
  }
  var report_install = getFacebookReport(response_install);
  
  // Facebook Like広告のレポート取得
  var response_like = getReportByHttpRequest(yesterday, "date_start,date_stop,campaign_id,campaign_name,reach,spend,clicks,unique_clicks,ctr,cpm,actions", PAGE_LIKE_CAMPAIGN_ID);
  if(response_like.getResponseCode() != 200) {
    Logger.log("Facebook Like広告レポートを取得できませんでした");
  }
  var report_like = getFacebookReport(response_like);
    
  if(report_install["data"] != "") {
    // App Install KPIをサマリーシートに出力
    sheet_Daily_facebook_install_KPI_2017.getRange("B" + target_row).setValue(report_install["data"][0]["date_start"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("C" + target_row).setValue(report_install["data"][0]["date_stop"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("D" + target_row).setValue(report_install["data"][0]["campaign_id"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("E" + target_row).setValue(report_install["data"][0]["campaign_name"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("F" + target_row).setValue(report_install["data"][0]["actions"][4]["value"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("G" + target_row).setValue(report_install["data"][0]["reach"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("H" + target_row).setValue(report_install["data"][0]["spend"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("I" + target_row).setValue(report_install["data"][0]["clicks"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("J" + target_row).setValue(report_install["data"][0]["unique_clicks"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("K" + target_row).setValue(report_install["data"][0]["ctr"]);
    sheet_Daily_facebook_install_KPI_2017.getRange("L" + target_row).setValue(report_install["data"][0]["cpm"]);
  } else {
    Logger.log("Facebook App Install広告レポートの値がありません");
  }
  
  if(report_like["data"] != "") {
    // Page Like KPIをサマリーシートに出力
    sheet_Daily_facebook_like_KPI_2017.getRange("B" + target_row).setValue(report_like["data"][0]["date_start"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("C" + target_row).setValue(report_like["data"][0]["date_stop"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("D" + target_row).setValue(report_like["data"][0]["campaign_id"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("E" + target_row).setValue(report_like["data"][0]["campaign_name"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("F" + target_row).setValue(report_like["data"][0]["actions"][4]["value"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("G" + target_row).setValue(report_like["data"][0]["reach"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("H" + target_row).setValue(report_like["data"][0]["spend"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("I" + target_row).setValue(report_like["data"][0]["clicks"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("J" + target_row).setValue(report_like["data"][0]["unique_clicks"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("K" + target_row).setValue(report_like["data"][0]["ctr"]);
    sheet_Daily_facebook_like_KPI_2017.getRange("L" + target_row).setValue(report_like["data"][0]["cpm"]);
  } else {
    Logger.log("Facebook Like広告レポートの値がありません");
  }
}
```

以下キャンペーンIDは、[広告マネージャで確認したID](#facebook%E5%BA%83%E5%91%8A%E3%82%AD%E3%83%A3%E3%83%B3%E3%83%9A%E3%83%BC%E3%83%B3id%E3%81%AE%E7%A2%BA%E8%AA%8D)を指定してください。

```
  // キャンペーンID
  var APP_INSTALL_CAMPAIGN_ID = "";
  var PAGE_LIKE_CAMPAIGN_ID = "";
```

指定するAPIのパラメータ(parameters)に関する詳細情報は、[こちらのFacebook公式リファレンス](https://developers.facebook.com/docs/marketing-api/insights/parameters/v2.8)を参考ください。

```
  var payload =
      {
        "time_range" : "{'since':'" + today + "','until':'" + today + "'}",
        "fields" : fields,
        "access_token" : ACCESS_TOKEN
      };
```

指定するAPIのフィールド(fields)に関する詳細情報は、[こちらのFacebook公式リファレンス](https://developers.facebook.com/docs/marketing-api/insights/fields/v2.8)を参考ください。

```
// Facebook App Install広告のレポート取得
  var response_install = getReportByHttpRequest(yesterday, "date_start,date_stop,campaign_id,campaign_name,reach,spend,clicks,unique_clicks,ctr,cpm,actions", APP_INSTALL_CAMPAIGN_ID);
```
```
// Facebook Like広告のレポート取得
  var response_like = getReportByHttpRequest(yesterday, "date_start,date_stop,campaign_id,campaign_name,reach,spend,clicks,unique_clicks,ctr,cpm,actions", PAGE_LIKE_CAMPAIGN_ID);
```  

KPIレポートの作成は、非同期でFacebook側で行われるため定期的に進行状況を問い合わせ、進行状況を確認する必要があります。そして、レポートの作成が完了後に、そのレポートIDを指定し、広告KPIを取得します。
詳細情報は、[こちらのFacebook公式リファレンス](https://developers.facebook.com/docs/marketing-api/insights/async/v2.8)を参考ください。

```
// 広告APIの取得結果は非同期なため進行状況を定期的に確認する
  var progress_percentage = getProgressByHttpRequest(report_run_id);
  Logger.log(progress_percentage);
  // 進行状況が100%未満の場合は、10秒スリープし、再度進行状況を確認する
  while (progress_percentage < 100) {
    Utilities.sleep(10000);
    progress_percentage = getProgressByHttpRequest(report_run_id);
    Logger.log(progress_percentage);
  }
```

なおもし該当のキャンペーンを実施していない場合はKPIは出力されません。

## GASの実行をスケジュール化
GASのスケジュール化は、以前記事にしているので[こちら](#gas%E3%81%AE%E5%AE%9F%E8%A1%8C%E3%82%92%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%8C%96)を参照ください。