慣れない言語に四苦八苦しながら(といいつつ、かなり楽しんでいる)、Amazon ECSからデータを引っ張ってくるクラスを書いてみた。
まだRubyに不慣れなので、かなりアレなコードだと思われるが、夢中になってるとブログに書くのも忘れるので、とりあえずのネタとして。
 require 'net/http'
 require 'cgi'
 require 'rexml/document'
 
 class AmazonEcs
   attr_reader :error_code, :error_message
   
   @@common_params = {
       :Service => 'AWSECommerceService',
       :AWSAccessKeyId => 'XXXXXXXXXXXXXXXXXXXX',
       :Version => '2007-05-14',
       :AssociateTag => 'xxxxxxxxxxx',
       :Operation => 'ItemSearch',
       :ResponseGroup => 'Medium,BrowseNodes'
     };
  
   def initialize(book)
     @sw = SearchWord.new
     @sw.set_by_book(book)
   end
   
   def item_search
     h = {}
     @@common_params.each do |key,val|
       h[key] = val
     end
     if @sw.isbn.blank?
       h[:SearchIndex] = 'Books'
       h[:Title] = @sw.title unless @sw.title.blank?
       h[:Author] = @sw.author unless @sw.author.blank?
       h[:Keyword] = @sw.keyword unless @sw.keyword.blank?
       @response_node_name = 'ItemSearchResponse'
     else
       h[:SearchIndex] = 'Books' if @sw.isbn.length > 10
       h[:IdType] = 'ISBN' if @sw.isbn.length > 10
       h[:Operation] = 'ItemLookup'
       h[:ItemId] = @sw.isbn
       @response_node_name = 'ItemLookupResponse'
     end
     url = mk_url(h)
     @xml = aws_request(url)
     if has_xml_error?()
       return nil
     end
     first_ecs_book()
   end
   
   def mk_url(h)
     # RESTで必要なパラメータをURL形式に変更
     arr = []
     h.each do |key,val|
       arr << key.to_s + "=" + CGI::escape(val.to_s)
     end
     paramString = arr.join('&')
     URI.parse("http://webservices.amazon.co.jp/onca/xml?" + paramString)
   end
   
   def aws_request(url)
     return REXML::Document.new(Net::HTTP.get(url))
   end
   
   def has_xml_error?
     # エラーチェック
     xpath = "ItemSearchResponse/Items/Request/Errors/Error"
     @xml.get_elements(xpath).each do |element|
       @error_code = element.text('Code')
       @error_message = element.text('Message')
       return true;
     end
     return false;
   end
   
   def first_ecs_book
     xpath = "#{@response_node_name}/Items/Item"
     @xml.get_elements(xpath).each do |element|
       ecs_book = EcsBook.new
       ecs_book.asin = element.text('ASIN')
       ecs_book.isbn = element.text('ItemAttributes/ISBN')
       ecs_book.title = element.text('ItemAttributes/Title')
       ecs_book.author = element.text('ItemAttributes/Author')
       ecs_book.manufacturer = element.text('ItemAttributes/Manufacturer')
       ecs_book.binding = element.text('ItemAttributes/Binding')
       ecs_book.publication_date = element.text('ItemAttributes/PublicationDate')
       ecs_book.small_image_url = element.text('SmallImage/URL')
       ecs_book.medium_image_url = element.text('MediumImage/URL')
       ecs_book.large_image_url = element.text('LargeImage/URL')
       ecs_book.detail_page_url = element.text('DetailPageURL')
       return ecs_book
     end
     nil
   end
 end
中で使われている、SearchWordとEcsBookというクラスはそれぞれ、画面からの検索情報を渡すためのプレースホルダー、およびAmazonECSからの結果を保持し、DBへ格納するためのActiveRecordです。
ISBNが指定されている場合は、優先してISBN検索をするようにしたのだが、最近買った本(具体的には泣き虫弱虫諸葛孔明第弐部)のISBNを入れてみたところ、うまくいかない。
この本はISBNが従来の10桁ではなく13桁になっている(詳しくはWikipediaを参照のこと)。
ISBNが11桁以上だったら、IdTypeパラメータをISBNに設定(このとき、SearchIndexパラメータも必要となる)すると、ItemLookupオペレーションが動くことを発見。ちなみに10桁のISBNは、ASINと一致なので、IdTypeはASIN、もしくは指定しない(=ASIN)である。ASINの場合、面倒だがSearchIndexパラメータが指定されると逆にエラーとなる。
APIドキュメントによると、JPではIdType=ISBNはサポートされてないことになっているが、使えるから良いか。
どうせなら10桁の時も、IdType=ISBNで取得させてくれれば楽だと思うのだが、そうはなっていない。