Amazon Product Advertising API を Python から使う

Amazon アソシエイト Web サービスの名称が、「Product Advertising API」に変わり、電子署名を 8月15日までにリクエストに含める必要がある旨のメールが先日届いた。

ama_py01

GAE/Python 上に、Amazon 情報取得処理を置くことを念頭に、リクエストのサンプルを作成してみる。

まず、参照すべきページ。

以下、簡単に 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 から参照可能

ama_py02

Your Accountメニュー – Account Identifies から・・・

ama_py03

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
  ama_py04

プロジェクトディレクトリの様子

ama_py05

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()
                );
            }
        }
    }
}

ama_py06

結果が帰ってきた・・・ のはいいけど、どうも、ぱっと見、今回問題としている 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 ボタンを押すと・・・ama_py07

Unsigned URL : 未署名のRESTリクエストが・・・

Name-Value Paires : KEY名と値のペアに分割され、TimeStampがなければ付与

ama_py08

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

http://ecs.amazonaws.jp/onca/xml?AWSAccessKeyId=1498TGK1YPN1JATPXXG2&AssociateTag=typea09-22&Keywords=%E6%89%8B%E5%A1%9A%E3%80%80%E6%B2%BB%E8%99%AB&Operation=ItemSearch&ResponseGroup=Medium&SearchIndex=Books&Service=AWSECommerceService&Timestamp=2009-07-29T15%3A57%3A09.593000Z&Signature=QwyP%2BmIsLJSoZZVEvO0ps5zPHfXZ1vevY/4sRiR7pAo%3D

ama_py09

正しく認証されて、結果が返ってきた。

また一歩野望に近づいた!

Follow me!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です