ISBNだけが知っている状態で,そこから書籍情報を機械的に取得したいのですが,ほんのちょびっとだけ調べていると,楽天APIの存在を知りました.
楽天には,ギフト購入や,旅行時の楽天トラベルで,よくお世話になっていて,会員登録をしています.デベロッパーIDの取得も簡単でした.APIに合うようURLを(Meadowで)書いて,ブラウザでアクセスし,うまく取得できるのを確認しました.
次はRubyで呼び出せるようにしましょう.RAAには…なさそう.でもスクリプトがありました.
- http://d.hatena.ne.jp/ikki_j/20080212/1202811398
- http://www.demenaoto.net/2009/04/23/ruby-on-railsror%E3%81%A7%E6%A5%BD%E5%A4%A9%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B/
ではこれを書籍検索に特化させましょう.
#!/usr/bin/env ruby # -*- coding: utf-8 -*- # rakuten-book-search.rb # http://www.demenaoto.net/2009/04/23/ruby-on-railsror%E3%81%A7%E6%A5%BD%E5%A4%A9%E3%82%A6%E3%82%A7%E3%83%96%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B/ # http://webservice.rakuten.co.jp/api/booksbooksearch/ require "rexml/document" require "open-uri" class RakutenBookSearch attr_reader :search_result def initialize(developer_id = nil, affiliate_id = nil) if developer_id @developer_id_query = "developerId=#{developer_id}" else return nil, "Developer IDをセットして下さい" end @affiliate_id_query = "&affiliateId=#{affiliate_id}" if affiliate_id @request_url = "http://api.rakuten.co.jp/rws/2.0/rest" end def book_search(options = nil) params_must = %w(title author publisherName size isbn booksGenreId) params_option = %w(hits page availability outOfStockFlag carrier genreInformationFlag) # must options if !(Hash === options) || (params_must.map {|item| item.to_sym} & options.keys).empty? return nil, "#{params_must.join(', ')}のいずれかをセットして下さい" end # options options_query = "" (params_must + params_option).each do |param| if options[param.to_sym] options_query += "&#{param}=#{options[param.to_sym]}" end end # sorting type if options[:sort] if %w(standard sales +releaseDate -releaseDate +itemPrice -itemPrice +reviewCount -reviewCount).include?(options[:sort]) options_query += "&sort=#{options[:sort]}" else return nil, "ソート方式が不正" end end options_query_encoded = URI.encode(options_query) # URI.encodeでは'=+'が変換されないので、手動で置換する options_query_encoded = options_query_encoded.gsub(/=\+/,'=%2B') @search_request = "#{@request_url}?#{@developer_id_query}#{@affiliate_id_query}#{options_query_encoded}&operation=BooksBookSearch&version=2009-04-15".gsub("\n","").gsub(" ","") $stderr.puts @search_request if $DEBUG xml_result = open(@search_request) @search_result, return_text = xml_to_array(xml_result) return @search_result, return_text end alias :item_search :book_search def xml_to_array(xml_result) doc = REXML::Document.new xml_result doc.elements.each("Response/header:Header/Status") do |status| unless status.text == "Success" return_text = status.text unless doc.elements["Response/header:Header/StatusMsg"] return_text += " " + doc.elements["Response/header:Header/StatusMsg"].text end return nil, return_text end end result = Hash.new doc.elements.each("Response/Body/*") do |ele| %w(count page first last hits carrier pageCount).each do |param| result[param.to_sym] = ele.elements[param].text.to_i end result[:item] = Array.new ele.elements.each("Items/Item") do |item| result_item = Hash.new %w(title titleKana subTitle subTitleKana seriesName seriesNameKana contents contentsKana author authorKana publisherName size isbn itemCaption salesDate itemPrice discountRate itemUrl affiliateUrl smallImageUrl mediumImageUrl largeImageUrl availability postageFlag limitedFlag reviewCount reviewAverage booksGenreId).each do |elname| el = item.elements[elname] result_item[elname.to_sym] = el.text if el end result[:item] << result_item end end return result, "" end end if __FILE__ == $0 search = RakutenBookSearch.new("ここに自分のDeveloper IDを書く") options = {:isbn => "4881665413"} result, error_message = search.item_search(options) if error_message.empty? p result else puts error_message end end
大きな違いは,書籍検索APIに合わせた入出力のパラメータ変更です.それと,元のスクリプトは,パラメータごとのURI追加や値の取得を一つ一つ書いていましたが,%w(...)で文字列のリストを作ってeachで順に処理し,文字列からシンボルを取得するにはString#to_symを利用しました.必須パラメータが一つもないことを,「(params_must.map {|item| item.to_sym} & options.keys).empty?」と,Array#&を使って求めました.
@search_requestへの最初の代入について,「….gsub!(...).gsub!(...)」というのは,gsubの挙動を考えるとおかしく(実際,手元のエラーが出ました),「….gsub(...).gsub(...)」に置き換えました.
結果は以下のとおり.(翌朝,手作業で改行を入れました.)
$ ruby rakuten-book-search.rb {:count=>1, :page=>1, :first=>1, :last=>1, :hits=>1, :carrier=>0, :pageCount=>1, :item=>[{:title=>"実践Ruby on Rails Webプログラミング入門", :titleKana=>"ジッセン ルビー オン レイルズ ウェブ プログラミング ニュウモン", :subTitle=>"無駄なく迅速な開発環境", :subTitleKana=>"ムダ ナク ジンソクナ カイハツ カンキョウ", :seriesName=>nil, :seriesNameKana=>nil, :contents=>nil, :contentsKana=>nil, :author=>"伊尾木将之", :authorKana=>"イオキ,マサユキ", :publisherName=>"ソーテック社", :size=>"単行本", :isbn=>"9784881665411", :itemCaption=>nil, :salesDate=>"2006-09-01", :itemPrice=>"2730", :discountRate=>"0", :itemUrl=>"http://item.rakuten.co.jp/book/4137945/", :affiliateUrl=>nil, :smallImageUrl=>"http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8816/88166541.jpg?_ex=64x64", :mediumImageUrl=>"http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8816/88166541.jpg?_ex=120x120", :largeImageUrl=>"http://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/8816/88166541.jpg?_ex=200x200", :availability=>"1", :postageFlag=>"1", :limitedFlag=>"0", :reviewCount=>"0", :reviewAverage=>"0.0"}]}
充実の内容です.
あっと,ISBN-10でリクエストを送っても,レスポンスのisbnの値は,13桁(ISBN-13)になっている点は,気をつけないといけませんね….