keepa APIのRequest ProductsでAmazon商品情報取得のやり方（Nodejs ver.）

keepa本家の英語の説明を読みたい方はこちら https://keepa.com/#!discuss/t/product-object/116

#コード
request-promiseを使用しています。 (requestと同じです。)
//# とついているところを下部で解説しています。

```keepa_sample_with_node.js
const rp = require('request-promise'); 

//リクエストを作成
const asins = ['B011111111','B022222222','B033333333']; //#2

const keepa_request = rp({
        url : 'http s://api.keepa.com/product',
        method : 'POST',
        form : { 
            key : 'APIキーを入力', //#1
            asin : asins.join(), //#2
            domain : '5', //#3
            offers : '20', //#4
            stock : '1', //#4
            rating : '1' //#5
        },
        gzip: true, //#6
        headers : {
         'Content-Type':'application/json',
         'Connection' : 'keep-alive' //#6
        },
        json : true
    });

//レスポンス
keepa_request.then((response) => {
                   
 const products = response.products; //response.products にそれぞれの商品のデータが配列で入ってる
 console.log(products.length); //=>3 3つリクエストしたのでレスポンスも3つ

for(var i=0;i<products.length;i++){
  var product = products[0];
  var m = 0;
  var arr = [];
  var offer = {};
  
  //よく使うもの
  console.log('商品のASIN: ' + product.asin);
  console.log('タイトル: ' + product.title);
  console.log('大カテゴリーID: ' + product.rootCategory);
  console.log('親ASIN: ' + product.parentAsin);
  console.log('EAN(JAN): ' + product.ean);
  console.log('ブランド: ' + product.brand);
  console.log('型番: ' + product.model);
  console.log('より詳細な型番: ' + product.partNumber);
  console.log('色: ' + product.color);
  console.log('サイズ: ' + product.size);
  console.log('取り扱い開始日: ' + product.releaseDate);
  console.log('最も長い辺の長さ（mm） : ' + product.packageLength);
  console.log('2番目に長い辺の長さ（mm）: ' + product.packageWidth);
  console.log('最も短い辺の長さ（mm）: ' + product.packageHeight);
  console.log('重量（g）: ' + product.packageWeight);
  console.log('Amazon本体の最新の出品価格: ' + product.csv[0][product.csv[0].length-1]);
  if(product.csv[1]) console.log('新品の最新の出品価格(最安値): ' + product.csv[1][product.csv[1].length-1]);
  if(product.csv[2]) console.log('中古の最新の出品価格(最安値): ' + product.csv[2][product.csv[2].length-1]);
  if(product.csv[3]) console.log('最新のランキング(大カテゴリ): ' + product.csv[3][product.csv[3].length-1]);

  if(product.imagesCSV){
    for(m = 0, arr = product.imagesCSV.split(','); m < arr.length; m++){
      Logger.log('画像URL-' + m + ': https://images-na.ssl-images-amazon.com/images/I/' + arr[m]);  //'https://images-na.ssl-images-amazon.com/images/I/' + '画像のID' で画像URLにできる
    }
  }

  for(m = 0; m < product.categories.length; m++){
    console.log('小カテゴリID-' + m + ': ' + product.categories[m]);
  }

  if(product.variationCSV){
    for(m = 0, arr=product.variationCSV.split(','); m < arr.length; m++){
      console.log('他のバリエーションのASIN-' + m + ': ' + arr[m]);
    }
  } else { console.log('他のバリエーションはありません'); }

  //ratingパラメーター設定時のみ取得可能
  if(product.csv[16]) console.log('最新のレート: ' + product.csv[16][product.csv[16].length-1]); //45 = ☆4.5
  
  //offerパラメーター設定時のみ取得可能
  if(product.offers){
    for(m = 0, arr = product.offers; m < arr.length; m++){
      offer = arr[m];
      
      console.log('offerId: ' + offer.offerId);
      console.log('セラーID: ' + offer.sellerId);
      console.log('FBA(Prime)ならtrue: ' + offer.isPrime);
      console.log('出荷できる状態(在庫切れ or 予約販売で無い)ならtrue: ' + offer.isShippable);
      console.log('合わせ買い対象商品ならtrue: ' + offer.isAddonItem);
      console.log('予約販売ならtrue: ' + offer.isPreorder);
      console.log('Amazon本体ならtrue: ' + offer.isAmazon);
      console.log('プライム限定商品ならtrue: ' + offer.isPrimeExcl);
      console.log('商品のコンディション: ' + offer.condition); //#
      console.log('コンディション説明: ' + offer.conditionComment);

      //stockパラメーター設定時のみ取得可能
      console.log('最新の在庫数: ' + offer.stockCSV[offer.stockCSV.length-1]);//#
      
      //下記不明です。勉強不足申し訳ありません。
      console.log('lastSeen: ' + offer.lastSeen);
      console.log('isMAP: ' + offer.isMAP);
      console.log('isWarehouseDeal: ' + offer.isWarehouseDeal);
      console.log('isScam: ' + offer.isScam);
      
      console.log('==================================');
    }
    console.log('最新のカート取得者のセラーID: ' + product.buyBoxSellerIdHistory[product.buyBoxSellerIdHistory.length-1]);
  }
  
  
  //以下、あまり使わないもの
  //KTM = Keepa Time minutes
  console.log('商品の販売タイプ: ' + product.productType);
  console.log('ドメインID リクエストで指定したものと同じ: ' + product.domainId);
  console.log('トラッキングが開始された時のKTM: ' + product.trackingSince);
  console.log('最後に商品の価格などの情報が更新された時のKTM: ' + product.lastUpdate);
  console.log('最後に商品のレビュー情報が更新された時のKTM: ' + product.lastRatingUpdate);
  console.log('最後に商品の価格が変更された時のKTM: ' + product.lastPriceChange);
  console.log('商品の説明: ' + product.description);
  console.log('商品タイプ: ' + product.type);
  console.log('メーカー: ' + product.manufacturer);
  console.log('レビューがあればtrue: ' + product.hasReviews);
  console.log('UPC: ' + product.upc);
  console.log('mpn: ' + product.mpn);
  
  if(product.frequentlyBoughtTogether){
    for(m = 0; m < product.frequentlyBoughtTogether.length; m++){
      Logger.log('「よく一緒に購入されている商品」に表示されているASIN-' + m + ': ' + product.frequentlyBoughtTogether[m]);
    }
  } else { Logger.log('「よく一緒に購入されている商品」に表示されているASINはありません'); }
  
  if(product.features){
    for(m = 0; m < product.features.length; m++){
      console.log('商品ページの箇条書き-' + m + ': ' + product.features[m]);
    }
  }
  
  //以下不明です。勉強中です。
  
  //
  //"eanList": String array,
  //"upcList": String array,
  //"label": String,
  //"department": String,
  //"publisher": String,
  //"productGroup": String,
  //"author": String,
  //"binding": String,
  //"numberOfItems": Integer,
  //"numberOfPages": Integer,
  //"publicationDate": Integer,
  //"languages":  two dimensional String array,
  //"studio": String,
  //"genre": String,
  //"edition": String,
  //"platform": String,
  //"format": String,
  //"hazardousMaterialType": Integer,
  //"newPriceIsMAP": Boolean,
  //"isEligibleForTradeIn": Boolean,
  //"isEligibleForSuperSaverShipping": Boolean,
  //"coupon": Integer array,
  //"isRedirectASIN": Boolean,
  //"isSNS": Boolean,
  //"offersSuccessful": Boolean,
  //"packageQuantity": Integer,
  //"isAdultProduct": Boolean,
  //"stats": Statistics Object,
  //"liveOffersOrder": Integer array, 
  
  console.log('---------------------------------------------------------------------------');
}
})

```

#POSTリクエストの作り方
##\#1 APIキーの確認方法
・[このページ](https://keepa.com/#!api) で自分のAPIキーを確認できます。　　
`key : 'APIキーを入力',`

##\#2 ASINの指定方法
最大100個まで指定できます。

ASINを配列にしてから、
`const asins = ['B011111111','B022222222','B033333333'];`

その後Array.join() でテキスト化して指定します。
`asin : asins.join(),`

※参考　ASINについて[公式ドキュメント](https://keepa.com/#!discuss/t/request-products/110)より引用
>\<ASIN\>
The ASIN of the product you want to request. For batch requests a comma separated list of ASINs (up to 100).

##\#3 ドメインIDはテキストで指定
情報を取得したい国のドメインをテキストで指定します。テキストで指定しないとエラーになるときがあります。
`domain : '5',`

指定できるドメインは下記の通り。日本なら 5 です。
**1: com**
2: co.uk
3: de
4: fr
**5: co.jp**
6: ca
7: cn
8: it
9: es
10: in
11: com.mx
12: com.br 

※参考　ドメインについて[公式ドキュメント](https://keepa.com/#!discuss/t/request-products/110)より引用
>\<domainId\>
Integer value for the Amazon locale you want to access. Valid values:
[ 1: com | 2: co.uk 5 | 3: de | 4: fr | 5: co.jp | 6: ca | 7: cn | 8: it | 9: es | 10: in | 11: com.mx | 12: com.br ]

##\#4 商品を出品しているセラーの情報も知りたい場合はoffersパラメーターを使用する
各セラーの価格やコンディション、FBA利用の有無なども取得したい場合はoffersパラメーターを設定してください。
このパラメーターを使用する場合、#2で指定できるASINは最大20個までになります。
20~100の範囲で設定できます。(10だけ必要な場合でも20必要です)
20と設定すると20オファーを取得します。

また、offerパラメーターを使用する場合、stockパラメーターに 1 を設定すると追加トークン不要で在庫数情報も取得できます。
※在庫が10個以上ある場合は10個までしか取得することができません。

`offer : '20',
 stock : '1',`

※参考　offersについて[公式ドキュメント](https://keepa.com/#!discuss/t/request-products/110)より引用
>**✜ offers**
Token cost: 5 for every found offer page (contains max. 10 offers)
Positive integer value. If specified it must be between 20 and 100. Determines the number of up-to-date marketplace offers to retrieve. The additional token cost is calculated by the number of found offers, not the requested amount (as a product can have less offers than requested). When using the offers parameter the basic 1 token cost per ASIN of the product request does not apply.

>If the offers parameter is used the product object will contain additional data:

>・Marketplace offer objects
・Information on the Buy Box, including a history of Buy Box winners.
Price History data for FBA (Fulfillment by Amazon), FBM (Fulfillment by Merchant), Warehouse Deals and all conditions, including shipping and handling costs.
・Rating and review count history.
・All offers-related data is updated independently, irregularly and not as often as all other product data. Keep this in mind when evaluating the additional historical data.

>**Note:**
・The returned product may have way more offers than specified. This is because we keep a history of offers. The number specified in this parameter determines how many offers we attempt to retrieve/update from Amazon. Each offer object has a lastSeen field that can be used to filter up-to-date offers.
・If used the max. batch size is reduced to 20 ASINs. Batched requests are processed in parallel.
・All offers related data is freshly updated and up-to-date.
・The request will require more time to complete; in the range of 2 and 20 seconds and an average of 5 seconds. Use batch requests or parallel requests to increase throughput when required.
・If we fail to retrieve/refresh the offers data the request will consume 1 token. The request will return successfully and will contain, if available, all historical offer and product data. You can retry the request after a few minutes.

>Limitations:
Not available for digital products, movie rentals and Amazon Fresh and Amazon Pantry.
Not available for Amazon China
Example: &offers=40 - Requested up to 40 offers, but the product only has 18 offers.
Total token cost for the request: five per found offer page = 10.

>**✜ stock**
No extra token cost. Boolean value (0 = false, 1 = true). Can only be used in conjunction with the offers parameter. If specified and has the value 1 the stock will be collected for all retrieved live offers. Note: We can only determine stock up 10 qty. Stock retrieval takes additional time, expect the request to take longer. Existing stock history will be included whether or not the stock parameter is used.

##\#5 レビュー情報も取得したい場合はratingパラメーターを使用する
レビュー情報も取得したい場合にはrating に 1 を設定してください。
現在取得できるのはレビューのレーティング(1.0~5.0)とレビューの個数のみのようです。
`rating : '1'`

※参考　ratingについて[公式ドキュメント](https://keepa.com/#!discuss/t/request-products/110)より引用
>**✜ rating**
Up to 1 extra token cost. Boolean value (0 = false, 1 = true). If specified and has the value 1 the product object will include our existing RATING and COUNT_REVIEWS history of the csv field, regardless if the offers parameter is used. The extra token will only be consumed if our last update to both data points is less than 14 days ago. Using this parameter does not trigger an update to those two fields, it only gives access to our existent data if available. If you need up-to-date data you have to use the offers parameter. Use this if you need access to the rating data, which may be outdated, but do not need any other data fields provided through the offers parameter to save tokens and speed up the request. If there is no rating data returned you can still make another request using the offers parameter.

>Example: &rating=1 (include the rating and review count data in the csv history data field of the product object and respective fields of statistics object)

##\#6 gzipとKeep-Aliveを使用する
必ずgzipを使用してください。（これに気づかずかなりハマりました）
`gzip: true,`

また、可能な限りKeep-Aliveを使用するべしとのことなので、ヘッダーで指定してください。
`headers : {
         'Content-Type':'application/json',
         'Connection' : 'keep-alive'
        },`


※参考　zipとkeep-aliveについて[公式ドキュメント](https://keepa.com/#!discuss/t/how-to-make-requests/767)より引用
>All requests must be issued as a HTTPS GET and accept gzip encoding. If possible, use a Keep-Alive connection.

(GET使えって言われてるけど、POSTでOKなようです)


#おまけ　GoogleAppsScript(GAS) でリクエストのやり方

GASはgzipをデフォルトでサポートしてます。

```keepa_sample_with_gas.gas.js
const asins = ['B011111111','B022222222','B033333333'];
const request =  UrlFetchApp.fetch('https://api.keepa.com/product',{
        method : 'POST',
        headers : { 'Connection' : 'keep-alive' },
        payload : {
          key : '自分のAPIキーを入力',
          domain : '5',
          asin : asins.join()
        },
      });
const products = JSON.parse(request.getContentText()).products;
```