2008/12/08

amazon-aws-with-ruby1.9.1

コマンドで ean を指定すると、amazon にアクセスしその本の情報を返してくれるものをつくってみた。
せっかくなので ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0] で作ってみた。
useaws.rb xxx を実行すると、xxxの番号が本の場合なら Book class のto_sを、CD の場合は Music classのto_sをかえす。
その他のメディアは作ってない。

%./useaws.rb 9784763198426
# =>
[Book][9784763198426][-][-]| アホは神の望み | 村上 和雄 | 2008 | ¥ 1,680


とか
% pathto/ruby/191 useaws.rb 9784763198426 > aho.txt
% open aho.txt
などして使う。

+++ memo +++

アマゾンからデータ(xml)を取得したデータは、utf-8 か?をチェックしてそうじゃなかったらutf-8にしないといけないんだけど、force じゃなくてもいいのかもしれない。

BaseItem#setup でハッシュのデータをもとにインスタンス変数を生成する際 Proc を使ってみた。使ってみたかったので使っているにすぎなくて適切かどうかはよくわかっていない。

カンマを付与するメソッド comma(n) は「Ruby Way」[第二版] P147に掲載されていた素晴らしいやつにちょっと挑戦してみた。「Ruby Way」ではわずか3行なのに自分のは長くてわかりにくい。本当は、もとのデータの price format をどこかに保存しておいてそれを to_s が使えるようにしてやればいいんだけど、ゆくゆくは marshal data にしたい思惑があったので、数字なら数字を、文字列ならば文字列を Book class or Music class に持っていてほしいと考えてるうちにこのように...

おかしな点などいろいろあるかと思われます

% cat -n aws.rb

     1	# -*- coding: utf-8 -*-
     2	# aws.rb
     3	# ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
     4	
     5	require 'rexml/document'
     6	require 'uri'
     7	require 'net/http'
     8	
     9	module AWS
    10	
    11	  class Amazon
    12	
    13	    def initialize(str=nil)
    14	      unless str.nil?
    15	        case str.encoding.name # String
    16	        when "UTF-8"
    17	          @xml = REXML::Document.new(str)
    18	        else
    19	          ustr = str.force_encoding("UTF-8")
    20	          @xml = REXML::Document.new(ustr)
    21	        end
    22	      end
    23	      @h = Hash.new
    24	      @i = nil
    25	    end
    26	
    27	    def item
    28	      dh = data_to_h
    29	      unless dh.nil?
    30	        @i = selectitem
    31	        return nil if @i.nil?
    32	        @i.setup.call(@h)
    33	        return @i
    34	      end
    35	    end
    36	
    37	    private
    38	
    39	    def selectitem
    40	      g = @h["ProductGroup"]
    41	      return Book.new() if g == "Book"
    42	      return Music.new() if g == "Music"
    43	    end
    44	
    45	    def data_to_h
    46	      return nil if @xml.nil?
    47	      return nil if @xml.to_s =~ /Error/ #or @xml.to_s.empty?
    48	      return nil unless @xml.encoding == "UTF-8"
    49	      getelement
    50	      set_data
    51	      return @h
    52	    end
    53	
    54	    def getelement
    55	      ei = @xml.root.elements["Items/Item"]
    56	      @attrib = get(ei,"ItemAttributes")
    57	      @img = get(ei,"MediumImage")
    58	      @url = get(ei,"DetailPageURL")
    59	    end
    60	
    61	    def set_data
    62	      @attrib.each{|x| @h[x.name] = plural(@attrib,x.name)}
    63	      @h.delete_if{|k,v| v.nil?}
    64	      setprice
    65	      setimg
    66	      seturl
    67	      @h["EAN"] = @h["EAN"].to_i
    68	      @h["ISBN"] = @h["ISBN"].to_i unless @h["ISBN"].nil?
    69	      @h["price"] = @h["price"].to_i
    70	    end
    71	
    72	    def get(ele,str)
    73	      ele.elements[str]
    74	    end
    75	
    76	    def plural(ele,str)
    77	      e = ele.get_elements(str)
    78	      case e.size
    79	      when 0
    80	      when 1
    81	        ele.elements[str].text
    82	      else
    83	        @h[str] = e.map{|i| i.text}.join(" / ")
    84	      end
    85	    end
    86	
    87	    def setprice
    88	      @h["price"] = @attrib.elements["ListPrice/FormattedPrice"].text.gsub(/\D/,'')
    89	    end
    90	
    91	    def setimg
    92	      @h["mediumimage"] = @img.elements["URL"].text unless @img.nil?
    93	    end
    94	
    95	    def seturl
    96	      url = exurl(@url.text) #url.encoding => <Encoding:UTF-8>
    97	      return nil unless url.encoding.name == "UTF-8"
    98	      eurl = url.gsub(/\?SubscriptionId=.*/u,'')
    99	      m = /amazon.co.jp\/(.*?\/)/u.match(eurl)
   100	      @h["url"] = eurl.gsub(m[1],'') # => "http://www.amazon.co.jp/dp/4274065979"
   101	    end
   102	
   103	    def exurl(string)
   104	      # reference: lib/ruby/1.9.0/cgi/util.rb #line.15
   105	      enc = string.encoding
   106	      string.gsub(/%([0-9a-fA-F]{2})/){[$1.delete('%')].pack("H*")}.force_encoding(enc)
   107	    end
   108	
   109	  end
   110	
   111	  class Seturi
   112	
   113	    def initialize(aws_accessKeyId=nil, associate_ID=nil)
   114	      @aws_key = aws_accessKeyId
   115	      @aws_id = associate_ID
   116	      @jp_ecs = 'http://ecs.amazonaws.jp'
   117	      #http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService
   118	    end
   119	
   120	    def seturi(ean)
   121	      return nil if @aws_key.nil?
   122	      return nil if @aws_id.nil?
   123	      uri = URI.parse(@jp_ecs)
   124	      uri.path='/onca/xml'
   125	      q = [
   126	        "Service=AWSECommerceService",
   127	        "AWSAccessKeyId=#{@aws_key}",
   128	        "Operation=ItemLookup",
   129	        "ItemId=#{ean}",
   130	        "AssociateTag=#{@aws_id}",
   131	        "ResponseGroup=Medium"
   132	      ]
   133	      ean = ean.to_s
   134	      case ean.size
   135	      when 10
   136	        q << ["SearchIndex=Books" ,"IdType=ISBN"]  # Book isbn 10
   137	      when 12
   138	        q << ["SearchIndex=Music", "IdType=EAN"]   # Not japanese CD
   139	      when 13
   140	        if ean.to_s =~ /^978|^491/                 # Book isbn 13 (EAN)
   141	          q << ["SearchIndex=Books" ,"IdType=EAN"]
   142	        elsif ean.to_s =~ /^458/                   # japanese DVD
   143	          q << ["SearchIndex=DVD", "IdType=EAN"]
   144	        else
   145	          q << ["SearchIndex=Music", "IdType=EAN"] # Japanese CD
   146	        end
   147	      end
   148	      uri.query = q.join("&")
   149	      return uri
   150	    end
   151	
   152	  end
   153	
   154	  class Access
   155	
   156	    def initialize(uri)
   157	      @host = uri.host
   158	      @request = uri.request_uri
   159	    end
   160	
   161	    def reponse
   162	      res = ""
   163	      begin
   164	        Net::HTTP.start(@host){|http|
   165	          response = http.get(@request)
   166	          res = response.body
   167	        }
   168	        sleep 2
   169	        return res
   170	      rescue SocketError
   171	        return 'socket'
   172	      end
   173	    end
   174	
   175	  end
   176	
   177	  class BaseItem
   178	
   179	    attr_reader :group,:ean, :title, :author, :price
   180	    attr_accessor :memo, :genru
   181	
   182	    def initialize
   183	      @adddate = Time.now.strftime("%Y-%m-%d %H:%M:%S")
   184	    end
   185	
   186	    def values
   187	      ary = []
   188	      self.instance_variables.each{|v|
   189	        val = instance_variable_get(v)
   190	        ary << val unless val.nil?
   191	      }
   192	      return ary.join("_")
   193	    end
   194	
   195	    def setup
   196	      Proc.new do |h|
   197	        h.each{|k,v|
   198	          s = "@#{k}".downcase.to_sym
   199	          set_ins(s,v)
   200	        }
   201	        set_ins(:@genru,'-')
   202	        set_ins(:@memo,'-')
   203	        nil
   204	      end
   205	    end
   206	
   207	    private
   208	
   209	    def set_ins(i,val)
   210	      val = val.encode("UTF-8") if val.kind_of?(String)
   211	      self.instance_variable_set(i, val)#unless val.nil?
   212	    end
   213	
   214	    def com_txt(ary)
   215	      str = String.new()
   216	      ary.each{|i|
   217	        val = instance_variable_get(i)
   218	        str << lines = i.to_s.gsub("@","--") + "\n" + val.to_s + "\n"
   219	      }
   220	      return str
   221	    end
   222	
   223	    def pubyear(y)
   224	      return n = /(\d...)/.match(y.to_s)
   225	    end
   226	
   227	    def comma(n)
   228	      s, ary, res, jsEn = n.size, [], [], "\u{A5} " # added space
   229	      k = (s - 4) % 3
   230	      n.to_s.each_char{|i| ary << i}
   231	      ary.each_index{|i|
   232	        res << ary[i]
   233	        k, res = k + 3, res << "," if i == k and i + 1 < s
   234	      }
   235	      return jsEn + res.join('')
   236	      ### Ruby Way P147.###
   237	      #str = n.to_s.reverse
   238	      #str.gsub!(/([0-9]{3})/,"\\1,")
   239	      #str.gsub(/,$/,"").reverse
   240	    end
   241	
   242	  end
   243	
   244	  class Book < BaseItem
   245	
   246	    def to_s
   247	      price_j = comma(@price)
   248	      per = person
   249	      n = pubyear(@publicationdate)
   250	      return printf "[%s][%013d][%s][%s]| %s | %s | %s | %s\n", @productgroup,@ean,@memo,@genru,@title,per,n,price_j
   251	    end
   252	
   253	    def person
   254	      ac = @author
   255	      ac = "#{@author}\s(#{@creator})" unless @creator.nil?
   256	      return ac
   257	    end
   258	
   259	    def to_txt
   260	      ary = [:@ean,:@title,:@author,:@creator,:@publisher,:@adddate,:@genru,:@memo]
   261	      ary.delete(:@creator) if @creator.nil?
   262	      com_txt(ary)
   263	    end
   264	
   265	  end
   266	
   267	  class Music < BaseItem
   268	
   269	    def to_s
   270	      price_j = comma(@price)
   271	      n = pubyear(@releasedate)
   272	      return printf "[%s][%013d][%s][%s]| %s | %s | %s | %s\n", @productgroup,@ean,@memo,@genru,@title,@artist,n,price_j
   273	    end
   274	
   275	    def to_txt
   276	      ary = [:@ean,:@title,:@artist,:@label,:@adddate,:@genru,:@memo]
   277	      com_txt(ary)
   278	    end
   279	
   280	  end
   281	
   282	end


% cat -n useaws.rb

     1	# -*- coding: utf-8 -*-
     2	
     3	# useaws.rb
     4	# Created by midore on 2008-12-06.
     5	# ruby 1.9.1 (2008-10-28 revision 19983) [i386-darwin9.5.0]
     6	
     7	require 'aws'
     8	
     9	class UseAmazon
    10	
    11	  include AWS
    12	
    13	  attr_accessor :ean, :aws_id, :aws_key
    14	
    15	  def initialize(ean=nil)
    16	    @ean = ean
    17	    @aws_key = nil
    18	    @aws_id = nil
    19	  end
    20	
    21	  def to_obj
    22	    doc = getdoc
    23	    return "socket error" if doc == "socket"
    24	    return "error doc" if doc.to_s =~ /Error/ or doc.empty?
    25	    a = Amazon.new(doc)
    26	    a.item
    27	  end
    28	
    29	  def getdoc
    30	    url = Seturi.new(@aws_key, @aws_id).seturi(@ean)
    31	    doc = Access.new(url).reponse
    32	  end
    33	
    34	end
    35	
    36	begin
    37	  ean =  ARGV[0] #ean = 9784797336610 if ean.nil? # 9784763198426
    38	  amazon = UseAmazon.new(ean)
    39	  amazon.aws_id = 'midore-22'
    40	  amazon.aws_key = 'xxxxxxxxxxxxx'
    41	  p ob = amazon.to_obj
    42	  puts ob.to_s
    43	  #puts ob.to_txt
    44	rescue
    45	  abort "Error\n"
    46	end


--imported_from
http://www.midore.net/daybook/2008/12/1228746600.html

0 件のコメント: