[GAS] AmazonのPrime Music聴き放題を一気に検索

# 現在　（2020/12/07時点）
最新のProductAdvertisingAPIは仕様がガラッと変わってますが
cURLでも叩けるようなので、理論上はGASで叩けるはずです。
https://webservices.amazon.com/paapi5/documentation/quick-start/using-curl.html

ただ、現在はAmazonアフィリエイトで30日間以内に売上実績が無いと
ProductAdvertisingAPIは使えないルールになっており、
私は売上実績が無いので試せません…。

__以下の記事は、古いものになります。__

# 概要
大量の楽曲リストのうち、Prime Musicで聴き放題に含まれている楽曲を抽出したかった。
実装はGoogle Apps Scriptで。表形式で楽曲リストを扱いやすいのと、外部API叩くのが簡単なため。
欠点としては実行時間に制限があること。それは手動で何度も実行することで解決(？)する。
Amazonの規約を守るため、Amazonアフィリエイトに登録してAdvertisingAPIを叩ける環境を各々用意のこと。
※[アイドル楽曲大賞2016]( http://www.esrp2.jp/ima/2016/ )の時に書いていたが、公開を忘れていた。。。
※Versionパラメータを渡すとエラーになるようなので外しました(2017/11/12更新)

# 実装
こちらの記事( [アマゾンのAPIを使って得た本の情報をGASのSpreadsheetに保存してみる](http://qiita.com/yooo_gooo/items/4c94b5914fcf9e8b3910) )を元にして、
ItemSearchを叩くようにしてある。
完全一致ではなく、ある程度曖昧な検索をしてくれているようなのでOUTPUTシートの内容については後から目視で吟味が必要。


Googleスプレッドシートは1行目を見出し行としてあらかじめ書いておく

INPUTシートの内容は処理が終わるとクリアされるので別に保存しておくこと
<img width="653" alt="INPUT_SHEET" src="https://qiita-image-store.s3.amazonaws.com/0/149787/d1298488-4f4a-102e-804d-c13c136a6b4e.png">

OUTPUTシートのEには、下記のような関数を記載（まぁ、わかると思うが…）
```
=CONCATENATE("<a target='amazon' href='",C2,"'>",B2," / ",A2,"</a>")
```
<img width="742" alt="OUTPUT_SHEET" src="https://qiita-image-store.s3.amazonaws.com/0/149787/d46812d6-22ae-7baf-055f-8c0fc0933cb5.png">


```JavaScript:PrimeMusic取得.gs
//Amazon検索・スプレッドシート書き込み処理
function searchAmazon(){
  var Inputsheet = SpreadsheetApp.getActive().getSheetByName("INPUT");
  var Outputsheet = SpreadsheetApp.getActive().getSheetByName("OUTPUT");

  //2行目以降を読み込み
  var colStartIndex = 1;
  var rowStartIndex = 2;
  var rowNum = 1;
  var lastRow = Inputsheet.getLastRow();
  var rowValues = [];
  
  //まとめて取得
  var range = Inputsheet.getRange(rowStartIndex, colStartIndex, lastRow, Inputsheet.getLastColumn());
  var values = range.getValues();
  var deletedRows = 0;
  var title = "";
  var artist = "";
  
  //出力
  var rowOutputIndex = Outputsheet.getLastRow() + 1;

  for(var rowIndex=0; rowIndex<values.length ; rowIndex++) {
      if(values[rowIndex][0]!=""){
        //1列めが空欄でない場合、データ有効とみなす
        title = values[rowIndex][0];
        artist = values[rowIndex][1];
        Logger.log(title);
        var results = getInfo(title,artist);
        Logger.log(results.length);
        for (var resIndex = 0;resIndex<results.length ; resIndex++){
          if(/(オリジナル歌手|オリジナルアーティスト|Originally Performed)/.test(results[resIndex].title)){
            //オルゴール系は排除
            continue;
          }
          Outputsheet.getRange(rowOutputIndex , 1).setValue(artist);
          Outputsheet.getRange(rowOutputIndex , 2).setValue(title);
          Outputsheet.getRange(rowOutputIndex , 3).setValue(results[resIndex].url);
          Outputsheet.getRange(rowOutputIndex , 4).setValue(results[resIndex].title);
          rowOutputIndex++;
        }
        //処理が終わったら削除
        Inputsheet.deleteRow(rowStartIndex);
        //スリープ
        Utilities.sleep(1000);
      }
  }

}

//Amazon APIとの通信
function getAmazonInfo(title,artist){
  var u = "http://ecs.amazonaws.jp/onca/xml?";
  var o = {
    Service:"AWSECommerceService",
    AssociateTag:"自分のアフィリエイトタグを記載",
    Operation:"ItemSearch",
    SearchIndex:"MP3Downloads",
    Timestamp:new Date().toISOString(),
    AWSAccessKeyId:"自分のアクセスキーIDを記載",
    ResponseGroup:"ItemAttributes",
    Sort:"salesrank",
    Title:title.replace(/(!|\(|\)|'|\*)/g,""),
    Keywords:artist.replace(/(!|\(|\)|'|\*)/g,""),
    BrowseNode:"3589137051" //PrimeMusicノードID、変更されていたら書き換える
  };
  var a = Object.keys(o).sort();
  a = a.map(function(key){
    return key +"="+encodeURIComponent(o[key]);
  });

  var s = "GET" + "\n" + "ecs.amazonaws.jp" + "\n" + "/onca/xml" + "\n" + a.join("&");

  var x = Utilities.base64Encode(Utilities.computeHmacSha256Signature(s, "自分のシグネチャを記載"));
  var z = u + a.join("&") + "&Signature=" + encodeURIComponent(x);
  var r;
  try{
    r = UrlFetchApp.fetch(z);
  } catch(e) {
    //あまり連続して叩くとエラーになるので、時間をおく。
    Utilities.sleep(1000);
    Logger.log(e);
    return getAmazonInfo(title,artist);
  }
  return XmlService.parse(r.getContentText());
}

//結果を扱いやすい形にして返す
function getInfo(title,artist){
  var result = [];
  var o = {};
  var x = getAmazonInfo(title,artist);
  var a = x.getDescendants();
  for(var i=0;i<a.length;i++){
    if(a[i].getType() == XmlService.ContentTypes.ELEMENT){
      switch(a[i].asElement().getName()){
        case "Title" :
          o.title = a[i].asElement().getText();
          if(o.url){
            result.push(o);
          }
          o = {};
          break;

        case "DetailPageURL" :
          o.url = a[i].asElement().getText();
          break; 
      }
    }
  }
  return result;
}




```




# 参考文献
[アマゾンのAPIを使って得た本の情報をGASのSpreadsheetに保存してみる](http://qiita.com/yooo_gooo/items/4c94b5914fcf9e8b3910)
[Product Advertising API:開発者ガイド＞ItemSearch](https://images-na.ssl-images-amazon.com/images/G/09/associates/paapi/dg/index.html?ItemSearch.html)