岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

Product Advertising APIの利用ガイドライン変更に対応した

ガイドラインの変更点

AmazonProduct Advertising APIの利用ガイドラインが変わりました。利用者にはお知らせメールが来ていると思います。僕には来ました。

変更点は下記の通りです。正確な内容はガイドラインをご参照ください。

  • 1時間に2000リクエストを上限とする(もっとリクエストしたければアソシエイトで売りまくれ)
  • リンクバックURLは組み立てるな。APIから返されたものをそのまま使え
  • APIアカウントとアソシエイトアカウントの登録メールアドレスを統一しろ
  • 一部のオペレーションやレスポンスグループは2010-10-15で廃止する
  • Multiple Operation RequestsやTextStream Search Parameterも2010-10-15で廃止する

リリースチェッカーの対応

僕が運営しているリリースチェッカーでもAPIを思いっきり利用しています。気が早いですが、ガイドラインに従うべく対応してみました。対応順に記します。

1. メールアドレスの統一

APIアカウントとアソシエイトアカウントの登録メールアドレスが別のものだったので、統一させました。ガイドラインからリンクされているページで変更できます。

2. RSSキャッシュ期間の変更

リリースチェッカーの生成する各RSSのキャッシュ期間を、30分から12時間に伸ばしました。リクエスト回数を減らすための措置です。リアルタイム性は求められないアプリケーションなので、一気に半日まで伸ばしました。様子を見て、もっと伸ばすかもしれません。

3. スリープ処理の変更

各リクエスト間のスリープを1.2秒から1.8秒に伸ばしました。3600秒÷2000回=1.8秒です。月に72,000円ぐらい売上があればもっと呼び出せるんですが、まず無理なので、2000回で計算しました。

ちなみに1.2秒にしていたのは、もともと「毎秒1コールを超えない」という規約があり、それに従っていたためです。開発当時、スリープをきっかり1秒にしていたら、エラーレスポンスが返るケースが頻発したため、1.2秒にした記憶があります。

4. 商品URLの変更

これまでは「http://www.amazon.co.jp/exec/obidos/ASIN/{ASIN}/{アソシエイトタグ}/ref=nosim」というURLを自前で組み立てていたんですが、APIのレスポンスに含まれるURLをそのまま使えとのことなので、そうしました。

リリースチェッカーをお使いの方は、がんがんRSSが更新されて驚かれているかもしれませんが、そういう事情です。すみません。

気になるのは、APIのレスポンスに含まれるURLがずっと固定なのかどうかです。これが流動的だと、URLが変わるたびにRSSが更新されてしまいます。もしそうであれば、リリースチェッカーの役目もそろそろ終わりなのかもしれません。

5. APIバージョンの変更

ガイドラインからリンクされているベストプラクティス・ガイドに「Use the Latest API Version」とあったので、リクエスト時のバージョンパラメータを「2009-11-01」に変更しました。

6. Multiple Operation Requestsの継続依頼

リリースチェッカーはMultiple Operation Requestsを活用しています。名前の通り、1回のリクエストで2つの処理をお願いできる便利な方式です。

これがなくなるとリクエスト回数が倍増するため、リリースチェッカーのパフォーマンスがかなり落ちるはずです。

「これらのAPIへのアクセスを引き続き必要とされるお客様は、お客様の事例を確認・検討させていただきますので、こちらまでご連絡下さい」とあったので、メールフォームで継続のお願いを送信しました。

所感

ガイドラインには「一部の開発者様が、非常に大量の商品データを要求しながら、非常に少ない商品の売上額しか発生させていないことが判明しています」と記載されています。これが今回の変更のきっかけということです。

「非常に大量の商品データを要求」するような開発者のなかには、規約(毎秒1コール)を気にせず富豪的にリクエストしていた人もいるのではないでしょうか。いろいろなアプリケーションで問題が起きそうな予感がします。僕の杞憂ならよいですが。

追記(2010-07-16)

APIのレスポンスに含まれるURLが長すぎて、RSSリーダーによってはうまく扱えないことが分かりました。bit.lyによるURL短縮を検討します。だんだんごつくなっていくなあ。

追記その2(2010-07-18)

bit.lyによるURL短縮処理を入れました。bit.lyにも呼び出し回数上限があるので、短縮URLが取得できなかった場合は長いままのURLを返すようにしています。

そういう次第で、またフィードががんがん更新されることになります。ユーザのみなさまは驚かれることと思いますが、あいすみません。

追記その3(2010-07-19)

ある程度は予想していたのですが、bit.lyのAPI呼び出し回数上限をあっさり超えてしまったため、URL独自組み立て方式に戻しました。なお、組み立てるURLは、「AmazonのアソシエイトID入りで一番短いURL | Creazy!」を参考に、「http://www.amazon.co.jp/o/ASIN/{ASIN}/{アソシエイトタグ}」のようにしています。

独自組み立て方式を採用すると、Product Advertising APIガイドラインに反してしまうことになります。そのデメリットは、いくら稼いでも2000回以上のリクエストが望めなくなるというものです(ガイドラインからリンクされている「よくあるご質問」を読んでの理解)。リリースチェッカーの場合、もともと2000回に収まるようプログラミングしているので、デメリットはまったくないことになります。

無駄な手間をかけてしまった感じですが、bit.lyの使い方が分かったので、よい勉強になりました。ユーザの方に迷惑をかけてしまったのは反省点です。

追記その4(2010-07-22)

http://developer.amazonwebservices.com/connect/message.jspa?messageID=187443#187443」を読んで知ったのですが、Multiple Operation RequestsとBatch Requestsは別物なんですね。同じ種類のリクエストをまとめたのがBatch Requests、違う種類のがMultiple Operation Requests。

リリースチェッカーで使っているのはBatch Requestsのほうでした。こちらが存続するのであれば、問題なしです。

HTTPの仕様書に「副作用」の定義がないのは不親切じゃね?

Webを支える技術』の101ページに、こう書かれています。

リソースの状態に変化を与えることを副作用(Side Effect)と言います

僕の読み落としでなければ、この定義の出典は同書には明記されていません。ただ、HTTPメソッドの冪等性や安全性を解説する文脈で紹介されているので、HTTP/1.1の仕様書であるRFC 2616が出典なのだろうと思います。

RFC 2616の本文には、「副作用」(side-effects あるいは side effects)という言葉が9回出てきます。最初に出てくるのは「9.1.1 Safe Methods」においてです。橋本英彦さんによる日本語訳を引用します(強調は岩本、以下同じ)。

本質的に、サーバが GET リクエストを実行した結果として副作用を起こさないという事を保証するのは不可能であり、事実、いくつかの動的なリソースはそれが特徴であると考えている。ここで特に区別すべきなのは、ユーザが副作用を要求しなかったという事であり、それゆえにそれらに対しては責任をもてない。

http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec9.1.1

「副作用」という言葉が、明確な定義なしに使われていることが分かります。

直前の段落を見てみます。

特に、GET と HEAD メソッドはその動作にリソースの回収以上の意味を持つべきではないという慣習が確立されている。これらのメソッドは、"安全{safe}" だと考えるべきである。これによって、ユーザエージェントがそれ以外の、例えば POST, PUT, DELETE のようなメソッドを特別な方法で表す事ができるようになり、ユーザにひょっとしたら安全でない動作が要求されているかもしれないという事実を認識させる。

http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec9.1.1

ここにおいて、「GET と HEAD メソッド」の副作用は「リソースの回収以上の」作用であると推測できます。

続く「9.1.2 Idempotent Methods」には、「副作用」という言葉が3つ出てきます。

メソッドは、(エラーや期限切れ発行とは別に) 同一のリクエストの N > 0 の副作用が単一のリクエストにおけるものと同じであるような際には "冪等{idempotence}" の性質を持つ事もできる。 GET, HEAD, PUT, DELETE 各メソッドはこの性質を共有する。また、OPTIONS と TRACE 各メソッドは副作用を持つべきではないし、本来冪等であるものである。

しかしながら、たとえその順序にて実行される全てのメソッドが冪等であったとしても、いくつかのリクエストの順番は冪等ではない。 (もし全体の順序中のある一つを実行しても、その順序の全体、または一部分を再実行した際に結果は常に変わらないという事になるのであれば、順序は冪等である。) 例えば、同じ順序でもその結果が後に修正される値に依存するのであれば、順序は冪等ではない。

副作用を持たない順序は、(同時に起こる動作は同じリソースの一組{set} 上に実行されている事は無い、との条件で) 定義によって冪等である。

http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec9.1.2

ここで僕は混乱します。PUT と DELETE の副作用とは、いったい何なのでしょうか。

冒頭に掲げた『Webを支える技術』によれば、「副作用」=「リソースの状態に変化を与えること」でした。PUT や DELETE の性質を考えれば、ここで使われている3つの「副作用」も、「リソースの状態に変化を与えること」を指しているのだろうと思います。

しかし、PUT や DELETE は「リソースの状態に変化を与えること」こそが「主作用」なのですから、それを「副作用」とよぶのは無理があるのではないでしょうか。

おそらくプログラミングにおける「副作用」、すなわち「状態の変化」を意味する言葉が流用されてしまったのだと思いますが、いずれにせよ、HTTPの仕様書に「副作用」の定義がないのは、なんとも不親切だと感じました。

以上のようなことを考えたきっかけは、徳丸さん(@ockeghem)による下記のツイートです。

副作用という用語はいつも使っていいか悩む。パスワード変更機能において、パスワードが変更されることが副作用で、「パスワードが変更されました」という表示が主作用(とは言わないが)というのは、直感に反する

徳丸 浩 on Twitter: "副作用という用語はいつも使っていいか悩む。パスワード変更機能において、パスワードが変更されることが副作用で、「パスワードが変更されました」という表示が主作用(とは言わないが)というのは、直感に反する"

セキュリティの文脈なので話が若干違うのかもしれませんが、このツイートを読んだときの僕の直感は、「パスワードが変更されること」こそが POST の主作用で、その処理結果がレスポンスボディに記される、というものでした。急に画面デザインが変わったり、サーバのCDトレイが開いたりするのが「副作用」のイメージ。

「リソースの状態に変化を与えること」を「副作用」以外の言葉で表せるといいんでしょうけれど、難しいですね。「リソース変化作用」じゃ長いかなあ。

Haskell初心者の僕が試したことをまとめてみた

なぜHaskellに興味をもったのか

僕がHaskellに興味をもったきっかけは、『まつもとゆきひろ コードの世界?スーパー・プログラマになる14の思考法』(通称「ホスト本」)でした。

ホスト本の第14章「関数型プログラミングについてなど」では、関数型プログラミングの特徴が4つ挙げられています。

  • 関数そのものをデータとして捉える(ファーストクラス関数)
  • 関数を引数として取る高階関数
  • 同じ引数を取る関数は同じ結果となる参照透明性
  • 参照透明性を実現するための副作用の忌避

このうち「参照透明性」が特に興味深く感じました。「同じ引数を取る関数は同じ結果となる」のですから、きっとテストが書きやすいはずです。「状態」によって結果が変わるオブジェクト指向言語ではテストが書きづらいと日頃から思っていたので、この参照透明性は福音のように感じられました。

その「参照透明性を実現するための副作用の忌避」が徹底されている言語がHaskellです。第14章では「Haskellのプログラムには基本的には副作用がありません」と書かれています。同章で紹介されている関数型言語には他にLispOCamlErlangがありますが、LispOCamlには副作用があります。Erlangは並列計算に特化しているため、今の僕の興味からは外れます。

以上のような理由で、Haskellに興味をもちました。

ちなみに、同じくまつもとさんが書かれた「総論 複数のプログラミング言語を学ぶ意義 - フリー言語で真のプログラミングを学ぶ:ITpro」では、Haskellが「世界を広げる言語」と評価されています。

GHCCentOSにインストールする手順

まずは環境作りということで、「HowTo:ProgrammingEnvironment」に従って、HaskellコンパイラであるGHCを手元のCentOS5に入れてみました。手順は下記の通りです。

$ wget http://haskell.org/ghc/dist/6.12.2/ghc-6.12.2-i386-unknown-linux-n.tar.bz2
$ tar xvf ghc-6.12.2-i386-unknown-linux-n.tar.bz2
$ cd ghc-6.12.2
$ ./configure
$ sudo make install

続いて、パッケージ管理用のコマンドであるcabalを入れました。事前にgmp-develとzlib-develのインストールが必要でした。

$ sudo yum install gmp-devel
$ sudo yum install zlib-devel
$ wget http://hackage.haskell.org/packages/archive/cabal-install/0.8.2/cabal-install-0.8.2.tar.gz
$ tar xvf cabal-install-0.8.2.tar.gz
$ cd cabal-install-0.8.2
$ sh bootstrap.sh

cabalのインストール後、「$HOME/.cabal/bin」にPATHを通しました。

Haskellプログラミングの参考にした本

文法の勉強には『ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門』が役立ちました。「cat」や「head」といったUNIXコマンドをHaskellで模してみるところから始まるため、先日『UNIXという考え方―その設計思想と哲学』を読んだ僕にはうってつけの内容でした。

第9章以降は難しかったので流し読みしました。必要になったらちゃんと読もうと思っています。

じつは先に『プログラミングHaskell』を買い、途中まで読んでいたのですが、挫折しました。僕には難しく感じられたためです。「ふつける」を読んだ今なら、読めるようになっているかもしれません。

初めてのHaskellプログラミングのお題

いよいよ、初めてのHaskellプログラミングです。AmazonProduct Advertising API利用時に必要な処理を題材に選びました。

Product Advertising APIの利用時には、パラメータ文字列と秘密キーでHMAC SHA-256を計算し、結果をbase64エンコードした署名が必要になります。詳しくは「アマゾンAPIを使うのに2009年8月15日から認証が必要になるらしい」をご参照ください。

この「パラメータ文字列と秘密キー」を引数に取り、「HMAC SHA-256を計算し、結果をbase64エンコードした署名」を返す関数を、Haskellで書いてみようという魂胆です。テストファーストが理想なのですが、早く書きたくて仕方がなかったので、実装から始めました。

試行錯誤の末、できたのが下記のコードです(いまひとつ自信がありません。おかしな点に気づかれたら、ぜひご指摘ください)。

module Main (main) where

import System.Environment (getArgs)
import Data.ByteString.Lazy.Char8 (pack)
import Data.Digest.Pure.SHA (hmacSha256, bytestringDigest)
import Data.ByteString.Lazy (unpack)
import Codec.Binary.Base64 (encode)

main :: IO ()
main = do
  args <- getArgs
  let secret_key = if length args >= 1 then args !! 0 else ""
  message <- getContents
  putStrLn $ sign secret_key message

sign :: String -> String -> String
sign secret_key message = encode $ unpack $ bytestringDigest $ hmacSha256 (pack secret_key) (pack message)

signが目的の関数です。ライブラリを利用しているだけですが、適切なライブラリを探すだけで一苦労でした。

追記(2010-07-26)

「適切なライブラリ」を書いてませんでしたね。dataencSHAです。

ちゃんと動くの?

上記のコードをコンパイルするには、必要なライブラリをcabalでインストールする必要があります。手順は下記の通りでした。

$ cabal update
$ cabal install dataenc
$ cabal install SHA

コードを「sign.hs」として保存、コンパイルして実行してみます。

$ ghc --make -o sign sign.hs
$ echo -n "GET\
>
> webservices.amazon.com\
>
> /onca/xml\
>
> AWSAccessKeyId=00000000000000000000&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews&Service=AWSECommerceService&Timestamp=2009-01-01T12%3A00%3A00Z&Version=2009-01-06" | ./sign 1234567890
Nace+U3Az4OhN7tISqgs1vdLBHBEijWcBeCqL5xN9xg=

Amazon アソシエイト(アフィリエイト) - ヘルプ」で示されている例と同じ文字列が得られたので、計算結果は問題なさそうです。

今後の予定

以上で、所期の目的が達成できました。今後は以下の内容に挑戦しようと思っています。

『横井軍平ゲーム館 RETURNS』は技術者にも響くはず

ごぶさたしております。思うところあって、最近、Haskell の勉強を始めました。勉強といっても、まだ本を読んでいるだけで、「関数脳」にはほど遠い状況です。そのうち、勉強の成果をこの日記に書きたいと思っています。

さて、今回の日記では『横井軍平ゲーム館 RETURNS』をご紹介します。本日入手し、一気に読んでしまいました。

本書が復刊されるという情報を得るまで、僕は、故・横井軍平氏にはさほど興味を持っていませんでした。が、なんとなく Wikipedia の「横井軍平」の項を読んでみたら、僕が子供のころに遊んだゲームの大半が彼の仕事だと分かったのです。テンビリオンも、ゲーム&ウォッチも、ファミコンも、ゲームボーイも、みんな彼の仕事だったので、愕然としました。

なぜ彼がヒット商品を連発できたのか、あるいは発売当時はヒットしなくても後になって評価されるような商品が開発できたのか、本書を読んで理解できたつもりです。商品開発に携わる方は読んで損はないと思います。

僕のような技術者には、「技術者は、見栄を捨てることが大切」(187ページ)、「技術者の陥りやすい誤った考え方」(194ページ)あたりの話がとくに参考になるはずです。

横井軍平ゲーム館 RETURNS ─ゲームボーイを生んだ発想力

横井軍平ゲーム館 RETURNS ─ゲームボーイを生んだ発想力

『UNIXという考え方』を読んだ

id:t-wada さんが:

UNIX という考え方』は、私のバイブルです。UNIX の背後に流れる価値観、設計思想、シンプルな設計とは何かを学べる、多くの技術者におすすめの本です。

QUnit-TAP : JavaScript のテスティングフレームワークQUnitからTAP出力する - t-wadaの日記

と書かれていたので読んでみました。

読んでよかった。和田さんに感謝です。和田さん大感謝祭です。もっと早くこの本に出会いたかった><

索引まで含めても148ページと薄い本ですが、得られたものはかなりの大きさです。今後、ぼくの設計判断ががらりと変わるのは間違いありません。

本書で解説されているのは、以下の「定理」です。ピンときた方には、一読をお勧めします。

  1. スモール・イズ・ビューティフル(小さいものは美しい)
  2. 一つのプログラムには一つのことをうまくやらせる
  3. できるだけ早く試作する
  4. 効率より移植性を優先する
  5. 数値データはASCIIフラットファイルに保存する
  6. ソフトウェアを梃子(てこ)として使う
  7. シェルスクリプトによって梃子の効果と移植性を高める
  8. 過度の対話的インターフェースを避ける
  9. すべてのプログラムをフィルタとして設計する

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

商品検索結果リソースのJSON表現を考えた

商品リソースに引き続き、商品検索結果リソースのJSON表現についても考えてみました。

まず、linkプロパティで「自分自身のURI」「前後ページのURI」「最初と最後のページのURI」を示すことにします。

続いて、totalResults、startIndex、itemsPerPage、Queryの各プロパティで、「商品数」「表示開始位置」「ページごとの商品表示数」「検索キーワード」を示すことにします。camelCaseにしたのは、これらのプロパティ名がOpenSearch由来だからです。それに引き換え、hProductの各フィールド名は「hyphen-separated-lowercase-words」で書くことになっています。

最後に、itemsプロパティで「該当商品リスト」を示すことにします。itemsというぐらいなので配列です。個々の商品の表現は、前回の商品リソースと同様とします。

結果、できたのが下記のJSON表現例です。itemsの中身をすべて記すと煩雑になるので、最初の商品以外は省略しています。


迷った点

リンク先のリソースが複数のフォーマット(ここではHTMLとJSON)で表現できる場合に、どちらにリンクすべきなのか、あるいはプラトニックな(フォーマット独立の)URIにリンクすべきなのか、しばらく迷いました。

linkプロパティについては、今のところ、どちらのフォーマットにもリンクさせています。プラトニックなURIにリンクさせて「300 Multiple Choices」を返す手もありますが、クライアントの立場に立つと処理が煩雑になる(したがってURIを組み立てたい誘惑にかられる)気がしたためです。

hProductのurlフィールドについては、複数のURIは指定できないため、HTML表現へのリンクにするか、JSON表現へのリンクにするか、プラトニックURIへのリンクにするかの三者択一となります。前掲の理由でプラトニックURIは却下、またJSONからHTMLにリンクするのもおかしな気がしたので、JSON表現へのリンクを選びました。

商品情報のJSON表現について考えた

キープリストで提供するリソースのURI

キープリストというWebアプリを作ろうとしています。気になる商品(Amazonで扱っているもの)を放り込んでおけるサービスです。

以前の日記では、URIの設計について考えました。それからも断続的に考えていて、現在は以下のようにしようと思っています。

リソースURI
トップレベルリソースhttp://keeplist.in/
商品検索結果リソースhttp://keeplist.in/amazonsearch?q={キーワード}&format={html または json}
商品リソースhttp://keeplist.in/Item:{ASIN}.{html または json}

本当はもっとたくさんのリソースが必要ですが、書き込み可能なWebサービスの設計は高難度なので、まずは読み取り専用部分のみ考えることにします。

さて、『Webを支える技術』で紹介されているROA(リソース指向アーキテクチャ)の設計手法に従えば、URI設計に続いてリソース表現の設計フェーズとなります。

どこから手を付けようか迷ったのですが、もっとも重要な商品リソースの、もっとも簡素なJSON表現から考えることにしました。

商品情報のJSON表現

microformatsには、商品情報を表現するhProductと、集計レビュー情報を表現するhReview-aggregateがあります。これらになるべく従うことにしました。

従うといっても、いずれもドラフト段階のフォーマットですし、microformats自体、JSONへの変換方法が明確に定義されていないので、どういう表現を提供するのか、結局は自分で考える必要があります。

試行錯誤してできたのが下記のJSON表現です。


迷った点

現在のドラフトでは、hReview-aggregate には item プロパティが必須なのですが、あえて無視しました。hProduct の review プロパティとして包含されているのですから、どの商品に対するレビューなのかは自明だからです。

最初は hProduct と hReview-aggregate を並列させて、Include Pattern でインクルードさせようかと思ったのですが、JSONでどう表現したらよいか分からなかったのでやめました。

hReview-aggregate に hProduct を包含させる方法も考えられますが、レビュー情報はおまけなので、コンテナにするのは無理がある気がします。Containers に関する議論が深まっていけば、このあたりの問題が解決されるのかもしれません。

表現例の改訂履歴

  • 表現例をgistに移行しました(2010-06-01)
  • hProduct から外れる商品情報(binding、creators など)を item-detail プロパティにまとめていましたが、まとめる必要がなかったので、ばらしました(2010-06-01)