コマンドで 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