Amazon Product Advertising API を Python から使う
Amazon アソシエイト Web サービスの名称が、「Product Advertising API」に変わり、電子署名を 8月15日までにリクエストに含める必要がある旨のメールが先日届いた。
GAE/Python 上に、Amazon 情報取得処理を置くことを念頭に、リクエストのサンプルを作成してみる。
まず、参照すべきページ。
- Amazon Product Advertising API スタートガイド
- Product Advertising API 開発者向けガイド リクエストの署名認証について(参考訳)
- APIリファレンス
以下、簡単に Product Advertising API を利用するための概要。
1.Amazon Web Services アカウントの取得
- Amazon Product Advertising API (長い・・・) を利用して、Amazonの情報を利用するためには、すべてのリクエストに、のAWSアクセス識別情報を含める必要がある。
- アクセス識別情報は、アルファベットと数字からなりリクエスト送信者を一意に識別する。
- AWS Access Key ID とAWS Secret Access Keyとがあり、アカウントの取得により、参照可能となる
- アカウントの取得はhttp://aws.amazon.com/から行い、取得すると、Your Accountメニュー – Account Identifies から参照可能
Your Accountメニュー – Account Identifies から・・・
AWS Access Key ID とAWS Secret Access Keyと参照できる
2.アソシエイトになる
- アソシエイトは自分のWebサイトからAmazonのサイトを参照し販売手数料を得る
- アソシエイトとなるには、Associate ID が必要
- Associate ID は登録した地域でのみ有効
- アソシエイトの登録は、https://affiliate.amazon.co.jp/ から
3.Java で リクエストを処理する
最終的にPythonで、サンプルを作成したが、最初Javaでやろうと思い途中まで確認したので一応手順をメモしておく。
webサービスを呼び出すことによって、Product Advertising API クライアントサイドライブラリスタブが自動生成される。
Eclipse で、AmazonWsStubプロジェクトを作成したとすると、以下の手順
(1) ディレクトリ構成
AmazonWsStub ├─bin ├─src └─jaxws-custom.xml |
(2)jaxws-custom.xml の内容
<jaxws:bindings wsdlLocation="http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsdl" xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"> <jaxws:enableWrapperStyle>false</jaxws:enableWrapperStyle> </jaxws:bindings>
(3) WebService Import を実行するコマンド
wsimport -d ./bin -s ./src -p com.ECS.client.jax http://ecs.amazonaws.com/AWSECommerceService/AWSECommerceService.wsdl -b jaxws-custom.xml
プロジェクトディレクトリの様子
Java クラス群が自動作成された
(4) サンプルを作成して動かしてみる。
package info.typea.test.ecs; import java.util.List; import com.ECS.client.jax.AWSECommerceService; import com.ECS.client.jax.AWSECommerceServicePortType; import com.ECS.client.jax.Item; import com.ECS.client.jax.ItemAttributes; import com.ECS.client.jax.ItemSearch; import com.ECS.client.jax.ItemSearchRequest; import com.ECS.client.jax.ItemSearchResponse; import com.ECS.client.jax.Items; public class ProductAdvertisingAPITest { public static void main(String[] args) { ProductAdvertisingAPITest me = new ProductAdvertisingAPITest(); me.itemSearchTest(); } public void itemSearchTest() { // サービスのセット AWSECommerceService service = new AWSECommerceService(); // サービスのポートをセット AWSECommerceServicePortType port = service.getAWSECommerceServicePort(); // オペレーションオブジェクトの取得 ItemSearchRequest request = new ItemSearchRequest(); // オブジェクトの設定 request.setSearchIndex("Books"); request.setKeywords("dog"); ItemSearch itemSearch = new ItemSearch(); itemSearch.setAWSAccessKeyId("*************"); // Dummy itemSearch.getRequest().add(request); // Webサービスを呼び出し応答を格納 ItemSearchResponse response = port.itemSearch(itemSearch); // 結果を表示 System.out.println("-- print item search result --"); List<Items> itemslist = response.getItems(); for (Items items : itemslist) { List<Item> itemlist = items.getItem(); for (Item item : itemlist) { ItemAttributes attr = item.getItemAttributes(); System.out.format( "%s %s %s %s\n", item.getASIN(), attr.getTitle(), attr.getAuthor().get(0), item.getDetailPageURL() ); } } } }
結果が帰ってきた・・・ のはいいけど、どうも、ぱっと見、今回問題としている Secret Access Key が現時点では指定できなさそう?SOAP?これに署名計算を埋め込む???まぁPythonを覚えたいので、Pythonでやってみよう。
4. Pythonでリクエストを作成する
ということで、Pythonでやってみる。今回は署名付きRESTリクエストを作成するところまで。
RESTリクエストの詳細は、http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?AnatomyOfaRESTRequest.html このあたりを参照。
基本的には、署名の作成方法に従った、RESTリクエストを作成する。
このサイトを大いに参考にさせていただいた感謝。
(1) 基本イメージ
Amazon Product Advertising API は ざっくり Operation として、行いたい操作(たとえばItemSearch)をパラメータ(たとえばKeywords)とともに指定したリクエストをRESTやSOAPで送信し、結果をXMLで取得する。
俺流amazonの作り方―Amazon Webサービス最新活用テクニック
まずは、署名をつけたリクエストが正しく受理されることが目標なので、Oparation 基底クラスにそのあたりの基本機能を実装し、個別の操作はその派生クラス(または汎用クラス)としてつど作っていけばよいかなと。
(2) 手順
署名の作成方法に、署名つきRESTリクエストの作成手順が載っているが、これだけでは実際わかりにくい。
Product Advertising API Signed Requests Helper というツールが提供されており(Web もしくは ダウンロード)、これを確認すると、上記署名の作成手順の途中経過が、順を追って確認できるようになっている。
AWS Access Key ID とAWS Secret Access Keyと未署名のRESTリクエストを入力して、Display Signed URL ボタンを押すと・・・
Unsigned URL : 未署名のRESTリクエストが・・・
Name-Value Paires : KEY名と値のペアに分割され、TimeStampがなければ付与
Sorted Pairs : ソートされ・・・
String-To-Sign : 電子署名用の文字列が生成され・・・
Signed URL : 署名されたURLが生成される
ので、この結果を確認しながら、同じ出力が得られるようにしていけばよい。
(3) Python サンプルソース
ItemSearch API を利用するサンプルを作成してみた。
基本的には、素直に、実装したつもり。
URL エンコード RFC 2396
タイムスタンプ : ISO 8601
あたりを参考に。
#!Python2.6 # -*- coding: utf-8 -*- import urllib from datetime import datetime import hashlib, hmac import base64 #@see: http://d.hatena.ne.jp/niiyan/20090509/1241884365 class Operation: __safe_chars = '-._~' __ecs_url = 'http://ecs.amazonaws.jp/onca/xml' __service = 'AWSECommerceService' __access_key_id = '***********' # Dummy __associate_tag = '*******' # Dummy __secret_access_key = '**********************' # Dummy __http_verb = 'GET' __value_of_host_header_in_lowercase = '/onca/xml' __http_request_uri = 'ecs.amazonaws.jp' def __init__(self): self.__param_map = {} def operation_name(self): return '' def request(self): self.set_parameter('Service', self.__service) self.set_parameter('AWSAccessKeyId', self.__access_key_id) self.set_parameter('AssociateTag', self.__associate_tag) self.set_parameter('Operation', self.operation_name()) self.set_parameter('Timestamp', datetime.utcnow().isoformat() + 'Z') #Name-Value Pairs n_v_pair_list = [] for key in self.__param_map.keys(): n_v_pair_list.append(urllib.quote(key, self.__safe_chars) + '=' + urllib.quote(self.__param_map[key], self.__safe_chars)) #Sorted Pairs n_v_pair_list.sort() request_parm_str = '&'.join(n_v_pair_list) #String-To-Sign sing_part_list = [self.__http_verb, self.__http_request_uri, self.__value_of_host_header_in_lowercase, request_parm_str] str_to_sign = '\n'.join(sing_part_list) hmac_digest = hmac.new(self.__secret_access_key, str_to_sign, hashlib.sha256).digest() base64_encoded = base64.b64encode(hmac_digest) signature = urllib.quote(base64_encoded); return '%s?%s&Signature=%s' % (self.__ecs_url, request_parm_str, signature) def set_parameter(self, key, value='', remove=False): if remove: del self.__param_map[key] else: self.__param_map[key] = value def response_group(self, value='', remove=False): self.set_parameter('ResponseGroup', value='Medium', remove=False) class ItemSearch(Operation): ''' @see: http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?ItemSearch.html ''' def operation_name(self): return 'ItemSearch' def keywords(self, value, remove=False): self.set_parameter('Keywords', value, remove) def search_index(self, value='Books', remove=False): self.set_parameter('SearchIndex', value, remove) request = ItemSearch() request.keywords('手塚 治虫') request.search_index('Books') request.response_group('Reviews') print request.request()
結果作成された URL
正しく認証されて、結果が返ってきた。
また一歩野望に近づいた!