【PHP】Yahoo!広告APIでレポートデータを取得したい！

#はじめに

GoogleAPIについて書いたので、ついでにYahoo!もメモ。

Yahoo!も従来の「Yahoo!プロモーション広告API」から「Yahoo!広告 API」と名称を一新し、
新APIが2020年2月20日に正式リリースされた。

これに合わせてサービスも名称を変更し、
「Yahooスポンサードサーチ」は「検索広告」に、「Yahooディスプレイ広告」は「ディスプレイ広告」に。

Googleと同じくoAuth2認証になった。ほかもいろいろ変わってるぽい。
個人的には認証以外にはそれほど大きく変わったわけではなさそう？という印象。

旧APIはスポンサードサーチが2020年9月30日（水）、
ディスプレイ広告が2020年12月16日（水）にサービス終了とのこと。
＃コロナの影響で延びる可能性あるかも・・・？

これもGoogleと同じく旧APIでCRONエラーが出たので調べてみたら
新API正式リリースとか言われて「えっうそ」といった感じで慌てて直した。。
リリースノートはちゃんと確認しないとイカンです・・・
＃っていうか運用担当者さんにメール来てただろうから教えてくれよと。。

リリース詳細は[こちら](https://ads-developers.yahoo.co.jp/developercenter/ja/announcement/192071.html)


従来のAPIを利用していた場合でも新たに申し込みが必要とのこと。
申込みが完了すると、検索・ディスプレイ両機能のテストアカウントIDももらえる。
（全然使ってないけど）

移行に関しては親切なドキュメントがあったので[スタートアップ](https://ads-developers.yahoo.co.jp/developercenter/ja/startup-guide/)を参照のこと。

<font color="Red">
[2021-02-03追記]
2/3現在の最新バージョンはv3。
最初に導入したv1は検索広告、ディスプレイ広告ともに3/22にシステム終了とのこと。</font>

<font color="Red">詳細は[リリースノート](https://ads-developers.yahoo.co.jp/developercenter/ja/developers-guide/release-note.html)参照。</font>

<font color="Red">新バージョンv3では、『2020/7/20 Yahoo! JAPAN Ads API Webinar』にて少し説明があったが、
微妙な違いのあったディスプレイ広告APIを検索広告APIに寄せた仕様の変更が加えられている。</font>

<font color="Red">それにともない、ディスプレイ広告API用のプログラムに修正を加えたので、
この記事の最後の方に追記しておく。</font>

#データ取得：oAuth2認証

さて、Yahoo新APIは旧よりも若干不親切で、[サンプルプログラム](https://ads-developers.yahoo.co.jp/developercenter/ja/developers-guide/sample-program.html)がJavaしか用意されていない。
旧APIはYahoo!のほうがGoogleより親切だと感じたんだけどな。
新はGoogleのがサンプル多くて使いやすそう。

認証はGoogleと同じ方式ということなので、横並びでやってみたらあっさり出来た。
※スタートアップに沿って設定あれこれは済ませてから！

以下、Yahooのアクセストークン取得処理。


```
//認証用URL
$version = "v1";

$oAuth2_url = "https://biz-oauth.yahoo.co.jp/oauth/$version/token";

//リフレッシュトークン
$refresh_token = "REFRESH_TOKEN";

//クライアントID
$client_id = "CLIENT_ID";

//クライアントシークレット
$client_secret = "CLIENT_SECRET";


//curl START
$curl = curl_init();

//OPTIONをセット
curl_setopt_array($curl, [
    CURLOPT_URL => $oAuth2_url,
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_POSTFIELDS => http_build_query([
        "refresh_token" => $refresh_token,
        "client_id" => $client_id,
        "client_secret" => $client_secret,
        "grant_type" => "refresh_token",
    ]),
]);

//curl EXEC(文字列で取得)
$resp =  curl_exec($curl);

//エラーハンドリング用
$errno = curl_errno($curl);

//curl END
curl_close($curl);

//エラーハンドリング
if ($errno !== CURLE_OK) {
    //エラー処理
}

//エラーでなければjsonを連想配列化        
$jsonresp = json_decode($resp, true);

//アクセストークンを取得
$access_token = $jsonresp["access_token"];

```

認証はYahoo検索/Yahooディスプレイ共通。
レポート取得となるとエンドポイントや処理を分ける必要がある。
＃ほとんど似た仕様なのだが、それぞれがびっっみょーに違うのでイラッとする。統一してくれ・・・


#データ取得：レポートデータ取得

続いてレポートデータ取得処理。

利用するリソースはReportDefinitionServiceというやつなのだが、
まず設定をaddして、getして、downloadして最後にremoveという手順が必要。非常にめんどい。。

※リソースの使用に関しては[リファレンス](https://yahoojp-marketing.github.io/ads-search-api-documents/)参照のこと

あと旧から仕様はそれほど変わらないらしく、ステータスがOKになるまで数秒waitする必要がある。
ここはGoogleと大きく違う部分。

前述のようにYahoo検索/ディスプレイでびっみょーに処理が違うけど、
基本的に同じなので、Yahoo検索の方を紹介していく。

##Yahoo検索の場合

```
//エンドポイント
$endpoint = "https://ads-search.yahooapis.jp/api";

//アカウントID
$account_id = "ACCOUNT_ID";

/**
 *  SELECT fields
 */
$YSSfields = [
    "DAY",
    "CAMPAIGN_NAME",
    "ADGROUP_NAME",
    "CLICKS",
    "COST",
    "AVG_CPC",
    "IMPS",
    "ADGROUP_ID",
    "CAMPAIGN_ID",
];



//ヘッダー情報をセット
$header = [
    "Content-Type: application/json", 
    "Accept: application/json",
    "Authorization: Bearer ".$access_token, 
];

//URLをセット
$url = $endpoint."/".$version."/ReportDefinitionService/";


//****************************
//  [1]add:reportJobIdを取得
//****************************
//add用URL
$url_add = $url."add";

//API用パラメータ配列[※ここでは日時を本日に指定]
$param_add = [
    "accountId" => $account_id,
    "operand" => [
        [
        "reportName" => "test",
        "reportType" => "ADGROUP",
        "reportDateRangeType" => "TODAY",
        "fields" => $YSSfields,
        ]
    ]
];

//curl START
$curl = curl_init();

//OPTIONをセット
curl_setopt_array($curl, [
    CURLOPT_URL => $url_add,
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => $header,
    CURLOPT_POSTFIELDS => json_encode($param_add),
]);

//curl EXEC(json文字列で取得)
$resp_add = curl_exec($curl);
$result_add = json_decode($resp_add, true);   //json文字列を連想配列化

//curl END
curl_close($curl);

//エラーハンドリング
$errno = ($result_add["errors"] !== NULL) ? $result_add["errors"]["code"] : NULL;
if ($errno !== NULL) {
    //エラーハンドリング
}

//reportJobIdを取得
$jobid = $result_add["rval"]["values"][0]["reportDefinition"]["reportJobId"];

//****************************
//  [2]get:reportデータを取得
//  JobStatusがCOMPLETEになるまでWAIT
//****************************
$statusflg = false;
for ($i = 0; $i < 30; $i++) {
    //30秒待機
    sleep(30);

    //get用URL
    $url_get = $url."get";

    //API用パラメータ配列
    $param_get = [
        "accountId" => $account_id,
        "reportJobIds" => [$jobid],
    ];

    //curl START
    $curl = curl_init();

    //OPTIONをセット
    curl_setopt_array($curl, [
        CURLOPT_URL => $url_get,
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => $header,
        CURLOPT_POSTFIELDS => json_encode($param_get),
    ]);

    //curl EXEC(文字列で取得)
    $resp_get = curl_exec($curl);
    $result_get = json_decode($resp_get, true);   //json文字列を連想配列化

    //curl END
    curl_close($curl);

    //エラーハンドリング
    $errno = ($result_get["errors"] !== NULL) ? $result_get["errors"]["code"] : NULL;
    if ($errno !== NULL) {
	    //エラーハンドリング
    }

    //ステータスコードを取得
    $status = $result_get["rval"]["values"][0]["reportDefinition"]["reportJobStatus"];

    //ステータスコードで処理を判別
    if ($status === "IN_PROGRESS" || $status === "WAIT") {
        continue;
    } else if ($status === "COMPLETED") {
        $statusflg = true;
        break;
    } else {
        break;
    }
}

//****************************
//  [3]download:reportデータをダウンロード
//****************************
if ($statusflg) {  //ステータスがCOMPLETEDの場合のみ処理
    //add用URL
    $url_dl = $url."download";

    //API用パラメータ配列
    $param_dl = [
        "accountId" => $account_id,
        "reportJobId" => $jobid,
    ];

    //curl START
    $curl = curl_init();

    //OPTIONをセット
    curl_setopt_array($curl, [
        CURLOPT_URL => $url_dl,
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => $header,
        CURLOPT_POSTFIELDS => json_encode($param_dl),
    ]);

    //curl EXEC(文字列で取得)******************************←ここにレポートデータ入ってる
    $resp_dl = curl_exec($curl);

    //curl END
    curl_close($curl);

    //エラーハンドリング
    $errno = ($result_dl["errors"] !== NULL) ? $result_dl["errors"]["code"] : NULL;
    if ($errno !== NULL) {
	    //エラーハンドリング
    }
}

//****************************
//  [4]remove:reportデータを削除
//****************************
//add用URL
$url_rm = $url."remove";

//API用パラメータ配列
$param_rm = [
    "accountId" => $account_id,
    "operand" => [
        [
        "reportJobId" => $jobid,
        ]
    ]
];

//curl START
$curl = curl_init();

//OPTIONをセット
curl_setopt_array($curl, [
    CURLOPT_URL => $url_rm,
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 120,
    CURLOPT_HTTPHEADER => $header,
    CURLOPT_POSTFIELDS => json_encode($param_rm),
]);

//curl EXEC(文字列で取得)
$resp_rm = curl_exec($curl);
$result_rm = json_decode($resp_rm, true);

//curl END
curl_close($curl);

//エラーハンドリング
$errno = ($result_rm["errors"] !== NULL) ? $result_rm["errors"]["code"] : NULL;
if ($errno !== NULL) {
    //エラーハンドリング
}

```

**長っ！**

**コード長っ！！**


たかだか本日のレポート取るだけでそんなに行数かかるかい？って思うんですけど、
現状で自分が解決できる方法がこれしかない。。

##Yahooディスプレイ広告の場合　※2021-02-03UPDATE

基本的には同じなんですが、違う部分はまず

1.エンドポイント（そりゃそうだよ）

```
//エンドポイント
$endpoint = "https://ads-display.yahooapis.jp/api";
```

2.~~POSTで渡すパラメータのKEY名や形式が微妙に違う。大文字小文字とか、ほんと微妙に。~~
<font color="red">v3で検索広告APIと同じになりました</font>

```
//add用パラメータ配列
$param_add = [
    "accountId" => $account_id,
    "operand" => [
        [
        "reportName" => "test",
//        "dateRangeType" => "TODAY",  //★これは旧バージョン
        "reportDateRangeType" => "TODAY",   //★こっちがv3
        "fields" => $YDNfields,
        ]
    ]
];
```

3.<2021-02-03 ADD>~~ステータスコードの配列名が違う。~~<font color="red">v3で検索広告APIと同じになりました</font>

```
//ステータスコードを取得
//$_status = $_result_get["rval"]["values"][0]["reportDefinition"]["jobStatus"];  //★これは旧バージョン
$_status = $_result_get["rval"]["values"][0]["reportDefinition"]["reportJobStatus"];   //★こっちがv3
```

4.~~あとgetでステータス取得する際のステータス名が微妙に違う。
WAITの代わりにACCEPTED~~<font color="red">v3で検索広告APIと同じになりました</font>

```
//ステータスコードで処理を判別
//if ($status === "IN_PROGRESS" || $status === "ACCEPTED") {  //★これは旧バージョン
if ($status === "IN_PROGRESS" || $status === "WAIT") {   //★こっちがv3
    continue;
} else if ($status === "COMPLETED") {
    $statusflg = true;
    break;
} else {
    break;
}


```

~~他にももしかしたら違いあったかも。微妙すぎてメモってないけど。
横並びで使ってみて動かなかったら一つずつ確認するって感じ。~~

<font color="red">
レポートに関しては、検索広告APIとディスプレイ広告APIで
ほぼ同じ内容に変更されたのではないかと思います。
ただ相変わらず、検索広告はレポートタイプを指定する必要があるけど、
ディスプレイ広告は指定しないという仕様はそのままなので、
その点だけは注意が必要ですね。。
</font>

#おわりに

Yahoo!の旧APIのサンプルプログラムを部分的に抜粋しながら作ったので、
それほど離れたことはやってない・・・はず。

今後、リファレンスやサンプルが充実してきたら、もっとスマートなやり方が分かるのかもしれない。
上記はあくまで我流だということをご承知おきいただければと。。

これからAPI導入する方が、少しでも参考にしてくれれば嬉しい。




今度はレポートデータではなくキーワードの更新などに取り掛かる予定。うまくいくといいなー