ラベル Amazon の投稿を表示しています。 すべての投稿を表示
ラベル Amazon の投稿を表示しています。 すべての投稿を表示

2010/07/31

attr_accessor

『プログラミングRuby1.9 言語編』(ISBN: 978-4-274-068096) を読みながら試してみたこと
# P.351 にクラスレベルのアクセサを定義する方法と解説がありました。

class Test
@var = 99
class << self
attr_accessor :var
end
end

puts "Original value = #{Test.var}"
Test.var = "cat"
puts "New value = #{Test.var}"

# 実行結果

Original value = 99
New value = cat


# 少し書き換えself を表示してみると

class Test
  p self
  @var = 99
  class << self
    p self
    attr_accessor :var
  end
end

# 実行結果

Test
#<Class:Test>
Original value = 99
New value = cat


# P.351 には

... 次のように特異クラス内で attr_accessor を呼び出す必要があります。

# とあるのだからして
# <Class:Test> が特異クラスなのだと理解していいのだろうか?
# #<Class:#<MyClass:xxxxx>> のような形式になっていないのが気になる...
# という疑問は一旦おいといて

# で、この class << self を使わないかぎり attr は定義できないのだろうか?
# class << self と書くのとは異なる方法はないのかな?

class Test1
  @var = 89
  self.class.attr_accessor :var
end

実行結果

# =>
./test.rb:30:in `<class:Test1>': private method `attr_accessor' called for Class:Class (NoMethodError)
  from ./test.rb:28:in `<main>'

# エラーだ。
# attr_accessor は <class:Test1> にとって private method だから外から呼べないよと言われてしまった。
# そんなメソッドは知らない、とは言われてない、てことは?

class Test2
  @var = 123
  self.class.send(:attr_accessor, :var)
end

puts "Original value = #{Test2.var}"
Test2.var = "dog"
puts "New value = #{Test2.var}"

# 実行結果

Original value = 123
New value = dog

# やったー
# では、これをinitialize の中でも使えるかな?

class Test3
  def initialize
    @var = "99-dayo"
    self.class.send(:attr_accessor, :var)
  end
end

t = Test3.new
puts "Original value = #{t.var}"
t.var = "apple"
puts "New value = #{t.var}"

# 実行結果

Original value = 99-dayo
New value = apple


# 使えた!ので ...

# @varなど予め定義されたインスタンス変数が不要な場合で(というか用意したハッシュの値を既定値にしたい)
# 尚かつインスタンス変数を定義する時になんら制限をもたせなくてよい条件下。
# ハッシュをうけとった class Test4 が
# 受け取ったハッシュをもとにインスタンス変数とアクセサを定義するには?

class Test4
  def initialize(h)
    @now ||= Time.now
    h.each{|k,v|
      ks = k.downcase.to_sym
      self.class.send(:attr_accessor, ks)
      self.send("#{ks}=", v)
    }
  end
end

h = {"Now"=>"123","Cat"=>"meow", "Dog"=>"bowwow"}
t = Test4.new(h)
p t, t.cat, t.dog

puts "----\n"
print "Instance_variables: #{t.instance_variables}\n"
print "Singleton: #{t.singleton_methods} \n"
print "Singleton_class: #{t.singleton_class}\n"

# 実行結果

#<Test4:0xxxxxx @now="123", @cat="meow", @dog="bowwow">
"meow"
"bowwow"
----
Instance_variables: [:@now, :@cat, :@dog]
Singleton: []
Singleton_class: #<Class:#<Test4:0xxxxxx>>


# あれーTest4 の中に singleton_class が存在しているのはなぜかしら
# self.class してる時点で自動的に特異クラスができあがっているのだろうか。

# 一瞬 object_id 同じなのか!?とぎょっとしたけど
# print "Object_id: #{t.object_id}, #{t.singleton_class.object_id}\n"
# を実行してみたら同じじゃなかった。

+++ memo +++

これ <http://midorex.blogspot.com/2010/05/selfsend2.html> を書いた頃...
ハッシュを受け取ったクラスがインスタンス変数を定義する際、アクセサによる一定の制限があればいいと考えた。
なぜなら Amazon から受け取る値の[全て]が必要ではないだろうと判断したから。

でも、全ての値をinstanceにしてもいっかと考えれば
アクセサによる制限は必要なくなるので上記のように書き換えてもいいのかもしれない..がよくわかっていない...。

<http://github.com/midore/get_item_amzon>

2010/04/21

2010-amazon-api-jp

SnowLeopard で OpenSSL がバージョンアップしたことに伴い
amazon-jp-api を作り直した。
=>
2010-amazon-jp-api

[参考になったURL]
OpenSSL SHA 256 の使い方はhttp://diaspar.jp/node/239 を参考にしました。

[主な変更点]
1, Mac OS X 10.6.3 で SHA256 を使えるようになったことに伴う変更。
module AmazonAuth => 新規に作ったモジュール。認証用。
2, 設定ファイル => 2つのモジュールにわけた。
3, class AwsItem => インスタンス変数の持ち方の変更。
4, class AmazonAccess => アマゾンにアクセスしデータを獲得したのちに AwsData.new(data).add_data でアイテムまで生成していたが、アイテム生成は他のクラスに移動させて、このクラスはアマゾンにアクセスしデータを獲得するだけのクラスに変更した。
5, class AwsDataWriter => 新規に作ったクラス。マーシャルデータを変更するためのクラス。
6, class AwsDataReader => 新規に作ったクラス。マーシャルデータを読み込んで表示させるためのクラス。
7, module AmazonAuth => 新規に作ったモジュール。アマゾン認証を行うためのもの 。

[変更されていない点]
1, ローカルで使うもの。
2, Ruby 1.9.1 だけで動き Ruby 1.8 系はサポートしていない。
3, マーシャルデータを使用している。
4, 指定したEAN の本やCDのデータをアマゾンから受け取ることができるがアマゾンサーバに対して検索はできない。
5, README に書いたオプション引数はそのまま。

[少しだけ気になっている点]
1, もともとのものは force_encodig を使わないように 変更している。今回もこの状態でうまくいったので受け取ったXMLデータが valid? かどうかだけチェックするにとどめ encode 変換は行っていない。このことによってなにか弊害があるのかないのかは謎。
2, timeout を使っている。使わないバージョンも作ってみたがあっさり挫折。
3, exec を使っている。exec を使わないバージョンを考えてみたがわからなくなって中断。
4, send を使っている。最近使い方を覚えたため、使ってみたかったから使っているにすぎない。あえて使う必要はないのかもしれない。

[このブログ内の関連する投稿]
=> http://midorex.blogspot.com/2009/06/amazon-web-with-ruby-191.html
=> http://midorex.blogspot.com/2009/08/amazon-product-advertising-api.html
=> http://midorex.blogspot.com/2009/12/forceencoding.html
=> http://midorex.blogspot.com/2008/12/amazon-aws-with-ruby191.html

[Amazon ラベルをもつ全ての投稿]
Amazon

2010/01/07

Kindle DX

Kindle DXのニュース検索結果

日本は電子ブック戦争になぜ敗れたのか
http://ascii.jp/elem/000/000/487/487838/
ふーん pdf よめない?
あれ、こっちには読めるとあった。

読めるの読めないのどっち?ってことはあとでゆっくり調べることにして、

それにしても、最初の ascii の記事2ページ目で気になったこと

しかし電子ブックには在庫リスクなんてないのだから、こんな不合理なシステムを守る必要はない。それなのに彼らはアマゾンの参入を求めようとしない...


この文章の意味、私にはさっぱりわかりませぬ。
彼らって取り次ぎのこと?
アマゾンの参入をもとめようとしない?はぁ?
アマゾンさんも取り次ぎ業一緒にはじめませんか?楽しいですよって営業にいくの?なあほなね。
それともアマゾンさん、商品全部全部 すべからく取り次ぎとーしてくださいよっ!て言うってこと?まさかね。

ま、どうでもいけど、
ここでいわれてる不合理なシステム、すなわち再販制度にとっては、kindle は黒船以外の何者でもないのでしょうね。

あんまりなんでちょっと追記。(2010-01-08)

第一に
Kindle の物理的な特徴は、アマゾンに参入を求めるとか「認めるとか認めないとか」、そんな従来の井戸の中の蛙的価値観の枠の中で語ることを不可能至らしめてしまうであろうこと、そして Kindle の日本上陸とは、そういったレベルの出来事なんだってこと、を池田氏はちゃんと認識しているのかな?と疑問を持ってます。

第二に不合理なシステム(再販制度)を持つ業界と電気産業とは基本的に別々の業界である以上、書籍の再販制度の不合理な点と、電気産業が独自の規格で我の支配を広げたがる話とは、似ているようで全く似ていない似て非なる話なのに、それをごっちゃにして日本側陣営のようにしてる点でも、首をかしげてしまってます。
むしろ、不合理なシステムが不合理であることはえらい昔からわかってたことなので、それを変えることができないまま今日に至っているのは、なぜなのかにきりこんで欲しかったです。

第三に日本は取り残されると心配する心情は理解できなくもないけれども、ダイナミックな本の歴史の流れからみれば、そんな心配はささいな支流にすぎず、電気産業に肩入れしただけの単なる杞憂にすぎないと思う。Kindle のもたらす影響を考えれば 電機メーカー心配するより出版業界心配すべきだろ、とあきれています。
日本の再販制度 vs アメリカ発電子本屋の市場競争の行く末は、どちらかが許容するだの認めるだのなんてあまっちょろいもんじゃないでしょうに。

それに電子ブック戦争はこの日本ではこれからはじまるのだから、日本の電機メーカーが敗れたって言ってしまう(記事のタイトルにおいて)のは早すぎる結論なんじゃないのかな。Sony は先進的すぎて成功できなかったかもしれないけど日本においては今がチャンス到来なわけですし。
もっとも、電子ブック戦争といえば、Apple の未来のデバイス vs Kindle を意味するようになっている時代がくるのかもしれません。

とまれ、池田氏のトーンは時代の変化を受け入れているように読めるので、その点「だけ」は好感を持ってます。

2009/12/15

2009/08/15

Amazon Product Advertising API

Amazon のAWS は 『Product Advertising API』に名称変更されました。この変更に伴い認証方法も変更されました。
私も github.com で AWS にアクセスするものを公開していたので今回の変更に対応してみました。

http://github.com/midore/amazon-jp-api

Ruby 1.9.1 でのみ動作します。

Mac OS X
ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-darwin9.7.0]
で動作確認してます。

使用する際には
Amazon アソシエイト ID と
開発者用アカウントが必要です。
具体的にいうと開発者用アカウントに付随する アクセスkey およびシークレットkey が必要です。

# 関連する投稿
Amazon Web サービスの署名認証 - with Ruby 1.9.1
http://midorex.blogspot.com/2009/06/amazon-web-with-ruby-191.html

2009/06/23

Amazon Web サービスの署名認証 - with Ruby 1.9.1

Amazon Product Advertising APIの認証の件
Amazon Product Advertising API (認証対応)

を読んで自分のID で試してみました。OS X のRuby 1.8.6 でちゃんと動きました。うれしい。
OS X 10.5.7 のOpenSSL のバージョンは古いので


dig = HMAC::SHA256.digest(secret_key, msg)

で試しました。

でも ruby 1.9.1 で動かすと


ruby-openid-2.1.6/lib/hmac/hmac.rb:94:in `ensure in digest': undefined method `reset_key' for nil:NilClass (NoMethodError)

となります。

あちこちみたあげく livedoor.com 経由で
amazon-auth-proxy
をみつけたのでこちらも手元で試してみました(cgi ぬきで)。module が拡張されていたのでもしや?と思いましたがやっぱり Ruby 1.8.6 では動くけどRuby 1.9.1 だと同じようなエラーがでて動きません。

エラーが指し示すライブラリ ruby-openid/lib/hmac/hmac.rb の中身をみて考えてみました。
reset_key メソッドはちゃんと存在しているし現にRuby 1.8.6 では動くので、一体なにがnil なのか?と少し書き足してエラーを再現してみたところ...


def set_key(key)
...
  for i in 0 .. key.size - 1
  key_xor_ipad[i] ^= key[i]
...

このメソッドにある ^= の部分でエラーになっていることがわかりました。Ruby 1.8.6 で動かすと nil じゃないけど、Ruby 1.9.1 だとnil になる。これはなぜだろう?あちこちに p をつけて実行してみて気づいたのは Ruby 1.9.1 の string[0]と Ruby 1.8 の string[0] の解釈が違うらしいということでした。

プログラミング言語 Ruby
p.59 によると
Ruby 1.8 の string[0]は、ASCII
Ruby 1.9 の string[0]は 文字列
とありました。

key[i]は、key に納められている文字列の1文字目の ASCII がかえってくる前提で書かれているけど、Ruby 1.9.1 で動かすと文字列そのもの("m"とか)がかえってきてしまう。これが nil になる原因なんだとわかりました。

...ということでいろいろ試しつつRuby 1.9.1 で動くものを作ってみました。とりあえず Amazon の認証にはクリアできましたが記述については自信がありません。

# 今回の Amazon からの宿題は Ruby 1.9.1 のString に関する知識を強制的に深める効果がありました。不幸中の幸いというかなんというか感謝。


# coding: utf-8

# Library:
# http://rubyforge.org/projects/ruby-openid/
# Original Page:
# http://github.com/tdtds/amazon-auth-proxy/blob/035bd4de90c4223994046fb489d7ccded566a879/amazon-auth-proxy.cgi
# http://diaspar.jp/node/239

# 使用したRubyのバージョン
# ruby 1.9.1p129 (2009-05-12 revision 23412) [i386-darwin9.7.0]

# Ruby 1.9.1 のgem は使ってない為ライブラリの絶対パスを push している。
# gem19 unpack ruby-openid-2.1.6.gem で解凍後のパス
oid = '/path/to/ruby-openid-2.1.6/lib'
$LOAD_PATH.push(oid)

require 'uri'
require 'time'
require 'openid'
require 'rexml/document'

module HMAC
 # Original
 # => http://github.com/tdtds/amazon-auth-proxy/blob/035bd4de90c4223994046fb489d7ccded566a879/amazon-auth-proxy.cgi
  # Ruby 1.9.1 用に改造してみる。
  IPAD = "\x36" * 64
  OPAD = "\x5c" * 64
  module_function
  def sha256( key, message )
    ipad, opad = [],[]
    # バイトごとにArrayに追加
  IPAD.each_byte{|x| ipad << x }
    OPAD.each_byte{|x| opad << x}
    ikey = ipad
    okey = opad
    akey = []
  # うけとったkey もArrayへ
    key.each_byte{|x| akey << x}
    key.size.times{|i|
      ikey[i] = akey[i] ^ ipad[i]
      okey[i] = akey[i] ^ opad[i]
    }
    # コードポイントから文字列へ
    ik = ikey.pack("C*")
    ok = okey.pack("C*")
    value = Digest::SHA256.digest( ik + message )
    value = Digest::SHA256.digest( ok + value )
  end

end

module Aws
  class Amazon

    def initialize(host, uri)
      @host = host
      @uri = uri
    end

    def escape(string)
   # Original
      # => ruby19/1.9.1/cgi/uri.rb CGI::escape(string)
      string.gsub(/([^ a-zA-Z0-9_.-]+)/) do
        '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
      end.tr(' ', '+')
    end
    
    def local_utc
      t = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
      escape(t)
    end
    
    def make_req(isbn, access_key, secret_key)
   # original はこちら => http://diaspar.jp/node/239
      req = 
      [
        "Service=AWSECommerceService",
        "Version=2009-03-31",
        "AWSAccessKeyId=#{access_key}",
        "Operation=ItemLookup",
        "SearchIndex=Books",
        "ResponseGroup=Medium",
        "IdType=ISBN",
        "ItemId=#{isbn}",
        "Timestamp=#{local_utc}"
       ].sort.join('&')
      msg = ["GET", @host, @uri, req].join("\n")
      # 拡張モジュール1.9.1用改造版呼び出し
      hash = HMAC::sha256(secret_key, msg)
      mh = [hash].pack("m").chomp 
      sig = escape(mh)
      return "#{@uri}?#{req}&Signature=#{sig}"
    end

  end
end

#-------------------#

accesskey = 'xxxxxxxxxxxxxxxxxxx'
secretkey = 'xxxxxxxxxxxxxxxxxxxx' 
host = 'webservices.amazon.co.jp'
path = '/onca/xml'

#isbn = "9784274066429"
#isbn = "9784839927844"
#isbn = "9784894712850"
isbn = "9784797336610"
req = Aws::Amazon.new(host, path).make_req(isbn, accesskey, secretkey)

#exit

# 署名認証リクエスト開始
begin
  Net::HTTP.start(host, 80) { |http|
    resp = http.get(req).body
  # ドキュメントをUTF-8 へ強制変換
    ustr = resp.force_encoding("UTF-8")
    doc = REXML::Document.new(ustr)
    doc.elements.each('ItemLookupResponse/Items/Item/ItemAttributes') { |item|
      puts '%s -> %s' % [item.text('EAN'), item.text('Title')]
    }
  }
end

結果


% ruby19 for-19.rb 
9784797336610 -> たのしいRuby 第2版 Rubyではじめる気軽なプログラミング

+++ 試したライブラリ+++
ruby-openid-2.1.6.gem
ruby-hmac-0.3.2.gem

+++ 参考にしたところ+++
Amazon Product Advertising APIの認証の件
Amazon Product Advertising API (認証対応)
github.com/tdtds

[を] アマゾンAPIを使うのに2009年8月15日から認証が必要になるらしい
KeN's GNU/Linux Diary | Amazon アソシエイト Web サービスの名称変更および署名認証についてのお知らせ

+++ Amazon +++
Product Advertising API 開発者向けガイド リクエストの署名認証について>(参考訳
http://developer.amazonwebservices.com/connect/ann.jspa?annID=442

+++ 追記 +++
Amazon Web Services Developer Community : OSSからの認証利用

私も Amazon Web サービスが元に戻ってくれたらいいのにな、と思いました。

2009/01/22

amazon-asociate-id

アマゾンアソシエイトリンクのアソシIDが有効かどうかを確認する方法
http://chalow.net/2009-01-13-4.html

を読んで変更したメソッド
mod_aws.rb

def seturl
  url = exurl(@url.text)
  return nil unless url.encoding.name == "UTF-8"
  tag = /\&(tag=.*?)\&/.match(url)[1]
  eurl = url.gsub(/\?SubscriptionId=.*/u,'')
  m = /amazon.co.jp\/(.*?\/)/u.match(eurl)
  @h["url"] = eurl.gsub(m[1],'') + "?" + tag 
  # =>"http://www.amazon.co.jp/dp/4274065979?tag=midore-22"
  #@h["url"] = eurl.gsub(m[1],'') # => "http://www.amazon.co.jp/dp/4274065979"
end


--imported_from
http://www.midore.net/daybook/2009/01/1232578805.html

+++ 追記 +++
2010-02-07
関連する記事
"Amazon"ラベルがついた全ての記事

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

2007/06/30

Book class for amazon

amazon 用のbook クラス考。テキストの中に isbn を探す始まるマークがみつかったら、Book クラスを呼び出す。
ItemLookup でamazon からのレスポンス XML は、Search で得られる結果とは異なり <Items><Item> 要素は1つ。指定したISBN がなければエラーXMLが返ってくる。

  item = Book.new(amazonxml).setup.item


amazon からのレスポンス xml を Book クラスに対して Book.new(xml) し...setup.item すると、Book の Struct で指定された要素を拾う。拾いたい要素のフルパスをコード内に記述しない方法の模索。

仮に<small><url>... <big><url>... といったように異なる親要素を持つ同一名の要素が レスポンス xml内 にあった場合で、
<big>要素の<url>要素 を拾いたい時、Book の Structに big_url とアンダーバーで要素名を連結するとその要素パスにマッチしたデータを探す。

Book クラス @h の中身は、{ [アマゾンXML の要素フルパス]=> 要素のテキスト ...]

#require 'time'
#require 'uri'
#require 'net/http'
#require "rexml/document"

class Book < Struct.new(:mediumimage_url, :detailpageurl, :author, :isbn, :publisher, :publicationdate, :title, :listprice_amount)

  def initialize(xml=nil)
    @getxml = REXML::Document.new(xml)
    #@tplxml = Tpl.new().tplamazon
    str = '<?xml version="1.0" encoding="utf-8"?>
    <?xml-stylesheet type="text/xsl" href="amazon.xsl"?>
    <docroot>
      <link rel="alternate" type="text/html" href="http://www.midore.net/"/>
      <modified/>
      <Items/>
    </docroot>'
    @tplxml = REXML::Document.new(str)
    @h = Hash.new
  end
  
  def errorcheck
    @tplxml = nil
  end

  def setup
    get_ele() if @h.empty?
    @h.each{|k,v|
      members.select{|x| self[x] = v if k.downcase.include?(x)}
    }
    #unless detailpageurl.nil?
      #durl = detailpageurl.scan(/\/.\/ASIN\/.*?%/).last.gsub("%",'') 
      #self.detailpageurl = 'http://www.amazon.co.jp' + durl
    #end
    return self
  end
  
  def item
    return nil if @tplxml.nil?
    item = REXML::Element.new('item')
    each_pair{|k,v| 
      item.add_element(k.to_s).add_text(v.to_s)
    }
    return item
  end

  def get_ele(ele=nil)
    ele = @getxml.root if ele.nil?
    errorcheck if ele.to_s.include?('Error')
    return nil if @tplxml.nil?
    ele.each{|x| 
        get_ele(x.entries) if x.has_elements?
      @h[x.xpath.to_s.gsub("/", '_')] ||= x.text unless x.text.nil?
      }
  end

end 

p Book.new(amazonxml)
#<struct May::AmazonAWS::Book mediumimage_url=nil, detailpageurl=nil, author=nil, isbn=nil, publisher=nil, publicationdate=nil, title=nil, listprice_amount=nil>

p item = Book.new(amazonxml).setup.item
item 要素に自分が欲しい要素だけを集めてある。
<item> ... </>

最終的な item 要素の中身

<item>
<mediumimage_url/>
<detailpageurl/>
<author/>
<isbn/>
<publisher/>
<publicationdate/>
<title/>
<listprice_amount/>
</item>


問題点 :
1, 著者が複数だった場合はどうする?
2, 画像サイズ指定し忘れた。
3, 表示させる要素に ISBN or EAN を含めるべきかどうか。その他、etc

--imported_from
http://www.midore.net/daybook/2007/06/1183167964.html

2007/06/06

Amazon Web Services 2

昨日作ったやつの答え合わせのつもりで検索検索。

natu_nの日記

Amazon ECS (Ver.2007-01-15) のドキュメント

Docs: Amazon E-Commerce Service (API Version: 2007-01-15)

ISBN(国際標準図書番号)規格改定に伴うアソシエイト・リンクの対応について [追加情報]

げげ?ISBNが13桁に?

ISBN(国際標準図書番号)規格改定等について お知らせ

(1) 2007年以降ISBNの規格は13桁とし、流通コードの国際標準であるEANコード(わが国ではJANコード)と一致させる。
http://www.isbn-center.jp/whatsnew/kikaku.html
(5) 2007年以降、現行のISBNは無効となる。


Type of product ID you are requesting information about.ISBN is for looking up books by their ISBN number. So, the ItemId must be a valid ISBN number and the search index in the request must be Books or ForeignBooks. The ISBN value must be 13 digits long and cannot include hyphens. The older 10-digit ISBN numbers can be converted in to the new 13-digit version by prepending "978" to the 10-digit ISBN number, which makes it 13 digits long.SKU requires a MerchantId. US only.UPC is US only.EAN is the same as JAN (Japanese article number), so you may use this parameter to look up items by JAN or EAN. DE/JP/CA only.If you select SKU, UPC, or EAN as the IdType for your request, you also need to include the SearchIndex parameter.


translation に頼ってみる。

ISBNは、彼らのISBN番号によって本を調べることに賛成です。それで、ItemIdは有効なISBN番号でなければなりません、そして、要請の検索インデックスはBooksまたはForeignBooksでなければなりません。ISBN価値は長さ13桁でなければならなくて、ハイフンを含むことができません。以前の10桁ISBN番号は10桁ISBN番号に「978」を前に付加することによって新しい13桁バージョンに中で変わることができます。そして、それはそれを13桁長くします。SKUは、MerchantIdを必要とします。USだけ。UPCは、USだけです。EANはJAN(日本の記事番号)と同じですので、あなたはJANまたはEANによってアイテムを調べるためにこのパラメータを使うかもしれません。DE/Japan paper/CAだけ。あなたがSKU、UPCまたはEANをあなたの要請のためのIdTypeとして選ぶならば、あなたもSearchIndexパラメータを含める必要があります。


2007 年以降は国際標準 EANだって読んだんだから、IdType=EAN を追加して実行してみると。

Result_ IdType=EAN # ISBN-13:9784797336610

リクエストには必要なパラメータの組み合わせが含まれていません。IdTypeがEANである場合、SearchIndexが必要です。


あっ、SearchIndex パラメータが必要だと書いてあったんだった。

> 検索インデックスはBooksまたはForeignBooksでなければなりません


公式ドキュメント_ SearchIndex

The Amazon store to search. Do not use this parameter with ASIN searches; an error will be returned.SearchIndex is required any time you select SKU, UPC, or EAN as the IdType for your request. The list of available SearchIndex values, segmented by locale, can be found on the search index values page.If IdType is set to EAN, SearchIndex is restricted to a select group of values by locale (Amazon.de and Amazon.co.jp).


やくす

捜すアマゾン店。ASIN検索でこのパラメータを使わないでください;エラーは返されます。あなたがSKU、UPCまたはEANをあなたの要請のためのIdTypeとして選ぶどんな時でも、SearchIndexは必要です。利用できるSearchIndex価値(現場によって分割される)のリストは、検索指数値ページで見つかることができます。IdTypeがEANにセットされるならば、SearchIndexは現場(Amazon.deとAmazon.co.jp)によって選ばれたグループの値に制限されます。


捜すアマゾン店。笑
「SearchIndexは現場(Amazon.deとAmazon.co.jp)によって選ばれたグループの値」てなんだろ。

Search Index Values
http://docs.amazonwebservices.com/AWSEcommerceService/2007-01-17/ApiReference/SearchIndexValues.html

SearchIndex=Books で試す。

Result_ IdType=EAN, SearchIndex=Books # ISBN-13: 9784797336610

9784797336610は、ItemIdの値として無効です。値を変更してから、再度リクエストを実行してください。

10桁ならとおる?
Result_ IdType=EAN, SearchIndex=Books # ISBN-10: 4797336617

4797336617は、ItemIdの値として無効です。値を変更してから、再度リクエストを実行してください。


おかしい。

公式ドキュメント_ItemId

Product(s) you would like information about. You may specify up to ten IDs using a comma-separated list (REST) or multiple elements (SOAP). By default the item IDs are assumed to be ASINs, unless you specify the IdType parameter.


やくす

あなたが情報が好きだろう製品(s)。あなたは、コンマで区切られたリスト(REST)または複数の要素(SOAP)を使用している最高10のIDを指定するかもしれません。あなたがIdTypeパラメータを指定しない限り、デフォルトで、アイテムIDはASINsであるとされます。


ItemId はIdTypeで指定されたTypeになると。何も指定しない時は ASIN になるよと。
IdType=EAN と指定したら必ずSearchIndexが必要と。その通りにしたつもりなんだけどな。Books じゃいけないのかなー。

どうすればいいのー?> google

実験結果をわかりやすくまとめてくださってました!。すばらしぃ!
HekatonCarefuleな日記

IdType=ISBN, SearchIndex=Books で13桁のISBNを指定したら成功とのこと。
13桁の ISBN を利用するには今のところ(2007/06) IdType=ISBN を指定する必要があるってことなんだね。EAN は使えないのかな。

Amazon Web Services Resource Center で検索
http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=59

http://developer.amazonwebservices.com/connect/entry.jspa?externalID=765&categoryID=17

The EAN field does not support ISBN-13 in the JP locale.


うわわ。IdType=EAN が指定できないのは日本ローカルな問題!?? だったのか。そか。そーだったのか。

日本のアマゾンさんは、なんでこの問題を日本語で明記してないのかなぁ。それとも私の探し方がたりないのかな。
「HekatonCarefuleな日記」さんが書かれているような内容は日本アマゾンの公式なページに掲載されていてしかるべき内容だと思うんだけど。あんのかなぁ。

+++++++++++++++++++++++++++++
まとめ。これでリクエストすれば13桁で検索できました。

        "Service=AWSECommerceService",
        "AWSAccessKeyId=#{@aws_key}",
        "Operation=ItemLookup",
        "ItemId=#{isbn}",
        "IdType=ISBN",
        "SearchIndex=Books",
        "AssociateTag=#{@aws_id}",
        "ResponseGroup=Medium"

けど、ISBN-10桁の本の情報を得たい時にはこれだとエラーになってしまうんでプログラム側でISBN桁数をみてからパラメータを分岐させるようにしないといけないということになりそ。
+++++++++++++++++++++++++++++

あらためて情報収集

本出している有名な方。Perl で書かれてありました。やっぱりパラメータはHekatonCarefuleな日記さんと同じでした。
Amazon Web Servicesを使ってISBN-13からASINを取得するPerlプログラム
http://d.hatena.ne.jp/hyuki/20070123#isbn

おや!?
"Version=2007-01-15" ひょっとしてひょっとしてこれを追加すれば10桁も可能になったりして?...それはありませんでした。
Result_ IdType=ISBN, SearchIndex=Books # ISBN-10: 4797336617

4797336617は、ItemIdの値として無効です。値を変更してから、再度リクエストを実行してください。


上級者の方発見。ちゃんとISBN 桁数で条件分岐もあるしエラーチェックもあってすごいです。答え合わせの答えが見つかった感じがしました。
http://www.iwazer.com/~iwazawa/diary/archives/003010.html
Version => '2007-05-14', ってかいてあった。あとで調べてみよう。

Amazon Web Services Resource Center
http://developer.amazonwebservices.com/connect/kbcategory.jspa?categoryID=59

Thread: 2007年からの和書のASINについて
http://developer.amazonwebservices.com/connect/thread.jspa?threadID=12010&tstart=0

--imported_from
http://www.midore.net/daybook/2007/06/1181131399.html