2009年5月10日日曜日

Amazon Product Advertising APIの署名認証をPythonでやってみる

AmazonアソシエイトWebサービスの名称が新しくProduct Advertising APIに変わるみたいです。

それだけなら、ふぅんという話なのですが、合わせて署名認証のお知らせがありました。

Amazon アソシエイト Web サービスの名称変更および署名認証についてのお知らせ

署名認証というのは正直初めて耳にしましたが、要するに自分とamazonだけが知っているパスワード(Secret Key)を利用して他人が不正利用できないようにすること、と理解しました。
あぁ、そういえばAmazonWebサービスに登録した時にSecret Keyというのがあったなと思い出しました。


Product Advertising APIを利用しているWebアプリはいくつかあるので、とりあえずPythonで作ったものから手をつけることに。


import urllib, urllib2, hmac, hashlib, base64
from datetime import datetime

class ProductAdvertising:

def __init__(self, access_key_id, secret_key, associate_tag=None, version='2008-08-19'):
self.access_key_id = access_key_id
self.secret_key = secret_key
self.associate_tag = associate_tag
self.version = version
self.uri = 'webservices.amazon.co.jp'
self.end_point = '/onca/xml'

def item_lookup(self, asin, options={}):
options['Operation'] = 'ItemLookup'
options['ItemId'] = asin
return self.send_request(options)

def send_request(self, options):
options['Service'] = 'AWSECommerceService'
options['AWSAccessKeyId'] = self.access_key_id
options['Version'] = self.version

# Timestampをセットしないとエラーになる。
# タイムスタンプはGMT(≒UTC)
options['Timestamp'] = datetime.utcnow().isoformat()

if self.associate_tag:
options['AssociateTag'] = self.associate_tag

# パラメーターをソートしてURLエンコード
# (修正 2009/05/17) payload = urllib.urlencode(sorted(options.items()))
payload = ""
for v in sorted(options.items()):
payload += '&%s=%s' % (v[0], urllib.quote(str(v[1])))
payload = payload[1:]

# 署名用の文字列
strings = ['GET', self.uri, self.end_point, payload]

# 署名の作成
digest = hmac.new(self.secret_key, '\n'.join(strings), hashlib.sha256).digest()
signature = base64.b64encode(digest)

url = "http://%s%s?%s&Signature=%s" % (self.uri, self.end_point, payload, urllib.quote_plus(signature))
return urllib2.urlopen(url).read()


使い方はこんな感じ
pa = ProductAdvertising(access_key_id='your_access_key_id', secret_key='your_secret_key')
item = pa.item_lookup('ASIN')


基本的には Product Advertising API: Example REST Requests ←こちらの例を参考にすれば問題ないはず。


ちなみに自分のSecret KeyはAmazon Web ServicesのYour AccountのAccess Identifiersで確認することができます。

PHPでもやってみました
Amazon Product Advertising APIの署名認証をPHPでやってみる


2009/05/17 修正

URLエンコード部分を下記のようにやっていましたが
payload = urllib.urlencode(sorted(options.items()))

urllib.urlencodeは内部でquote_plus使用しているようです。
quote_plusは空白文字をプラス(+)に変換します。
hmacの文字列にプラスが含まれているとアマゾンから
The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

という、署名が間違っているぞというエラーが返ってきますのでurllib.quoteでURLエンコードをおこなうように修正しました。

payload = ""
for v in sorted(options.items()):
payload += '&%s=%s' % (v[0], urllib.quote(str(v[1])))
payload = payload[1:]

7 件のコメント:

Keiya さんのコメント...

はじめまして!

Product Advertising APIで認証が必要になったと聞いて途方に暮れ、Googleで検索したところ、トップにでていたので、参考にさせていただきました。ちょうど、開発していたのが、Pythonのアプリケーションだったので、大変参考になりました。というか、Pythonだったことに大変驚きました。

ありがとうございました。

Youthhr さんのコメント...

コメントありがとうございます!

日本語でPythonを語ってくれるブログは少ないので大変ですよね。
お役に立ててなによりです。

keiichi さんのコメント...

こんにちは。

「10日でおぼえる Python 入門教室」という本にAmazon APIを使う例題があるのですが、古いAPIなので例の通りに動かすことができずに困っていました。
ここのスクリプトを参考にして作ってみたら、うまくできました。
とても助かりました。

funafuna38 さんのコメント...

keiichi さんと同じく10日でおぼえるpythonの、Amazon API を作成するstepでつまづいてお邪魔しました。とても参考になり、大変感謝しております。ありがとうございます。

ひとつ質問をさせていただきたいのですが、差し支えなければ教えてくださいませ。

それは、以下のようにサブルーチンを分けているのはなぜなのだろうかということです。

def item_lookup(self, asin):
options={}
options['Operation'] = 'ItemLookup'
options['ItemId'] = asin
return self.send_request(options)

def send_request(self, options):


ためしに、以下の部分だけを削除してサブルーチンをひとつにしてみても動くようでした。

return self.send_request(options)

def send_request(self, options):


プログラム自体勉強しはじめなので的はずれな質問をしているのかもしれませんが、よろしければご教示くださいませ。

Youthhr さんのコメント...

Keiyaさんコメントありがとうございます。

確かに上の例だとわざわざsend_requestを分けている理由が分かりにくいかもしれません。

ただ、Product Advertising APIのドキュメントを見ると分かりますが、Product Advertising APIにはたくさんのOperationがあります。
(商品を検索するのとかBrowsenodeをひっぱってくるのとか)

上の例(item_lookup)ではid(asin)を指定して個別の商品情報を取得するOperationだけを取り上げましたが、実際にはitem_searchとかlist_lookupなどのようにOperationごとのメソッドを用意することになるかと思います。
(その中でsend_requestを実行するわけです)

ご理解いただけたでしょうか?

funafuna38 さんのコメント...

ご教示いただき感激です!

わかりやすく説明していただいたので理解することができました。ありがとうございました!

gama さんのコメント...

PA APIのXSLTサービスでスクリプトを利用させてもらっています。

「パラメーターをソートしてURLエンコード」の際に urllib.quote()のsafeパラメータが指定されていませんが、これですと'/'がエンコードされないため、XSLTサービスでSignatureDoesNotMatchとなります。この部分と、リクエストを送るuriを変更するだけで、XSLTサービスでも使えました。