Fork me on GitHub

2008/05/12

はてな
結果から言うと実に使いにくい!
PyAWS - A Python wrapper for Amazon Web Service

PyAWS is a Python wrapper for the latest Amazon Web Service. It is designed to pave the way for Python developers to interactivate AWS. This project is forked from the code base of pyamazon. The Amazone E-Commerce Services is supported.

http://pyaws.sourceforge.net/
配列なら配列で、存在したり存在しない場合があるプロパティならばそれ用のアクセサを作ってほしい...
Djangoテンプレートで配列かどうかを判断してループで回してってのはつらいです。
あと、Django Template Engineの最新版でないと
{% for key, value in values %}
こういう書き方が出来ないので、結局明示的な名前の付いたdictに作り変える必要があった。まぁこれはpyawsのせいではないけれど。
aws.py
#!-*- coding:utf-8 -*-
import os
import cgi
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
import logging
import yaml
from pyaws import ecs

def to_dict_array(array):
  ret = []
  for item in array:
    ret.append({'key': item.keys()[0], 'value':item.values()[0] })
  return ret

class MainPage(webapp.RequestHandler):
  config = yaml.safe_load(open(os.path.join(os.path.dirname(__file__), 'aws.yaml'), 'r'))
  def get(self):
    kind = self.request.get('kind')
    keyword = self.request.get('keyword')
    template_values = {}
    template_values = {
      'asoid'    : self.config['asoid'],
      'kinds'    : to_dict_array(self.config['kinds']),
      'kind'     : kind,
      'keyword'  : keyword,
      'books'    : [],
    }
    if keyword:
      asoid = self.config['asoid']
      devkey = self.config['devkey']
      kinds = self.config['kinds']
      ecs.setLicenseKey(devkey)
      ecs.setLocale(self.config['locale'])
      books = ecs.ItemSearch(keyword, SearchIndex=kind, ResponseGroup='Medium,Offers')
      count = 0
      max = 5
      for book in books:
        if 'Author' in book.__dict__ and not isinstance(book.Author, list):
          book.Author = [book.Author]
        if 'Offer' in book.Offers.__dict__:
          if isinstance(book.Offers.Offer, list):
            book.Availability = book.Offers.Offer[0].OfferListing.Availability
          elif 'Availability' in book.Offers.Offer.OfferListing.__dict__:
            book.Availability = book.Offers.Offer.OfferListing.Availability
        template_values['books'].append(book)
        count += 1
        if count > max: break
    path = os.path.join(os.path.dirname(__file__), 'aws.html')
    self.response.out.write(template.render(path, template_values))

def main():
  application = webapp.WSGIApplication([('/pyaws/', MainPage)], debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()
aws.html
<html>
<head>
<title>AWS商品検索</title>
<style tyle="text/css"><!--
body {
    font-family: 'メイリオ', 'Osaka'
}
#content {
    margin-left: 50px;
}
#error {
    color: red;
}
.awsxom {
    background: #eeeeee;
    padding: 0.5em;
}
--></style>
</head>
<body>
    <h1>AWS商品検索</h1><img src="http://b.hatena.ne.jp/entry/image/http://mattn.appspot.com/pyaws/" title="はてなブックマーク" />
    <div id="content">
        <p align="right"><a href="/">目次</a></p>
        <form method="get">
            <label for="kind">種類</label>
            <select id="kind" name="kind" value="{{ kind }}">
                {% for item in kinds %}<option value="{{ item.key }}"{% ifequal item.key kind %} selected="selected"{% endifequal %}>{{ item.value }}</option>
                {% endfor %}
            </select>
            <label for="keyword">キーワード</label><input id="keyword" name="keyword" type="text" value="{{ keyword|escape }}" />
            <input type="submit" />
        </form>
        <hr />
        {% for book in books %}
        <div class="awsxom">
            <a href="http://www.amazon.co.jp/exec/obidos/ASIN/{{ book.ASIN }}/ref=nosim/{{ asoid }}">
                <img src="{{ book.SmallImage.URL }}" align="left" hspace="5" border="0" alt="{{ book.Title }}" class="image" />
                <strong>{{ book.Title }}</strong></a><br />
            {{ book.Author|join:", " }}<br />
            {{ book.Manufacturer}} / {{ book.ListPrice.FormattedPrice }} ({% if book.PublicationDate %}{{ book.PublicationDate }}{% else %}{{ book.ReleaseDate }}{% endif %})<br />
            &nbsp;<br />
            発送可能時間:{{ book.Availability }}<br />
            <br clear="all" />
        </div><br />
        {% endfor %}
    </div>
</body>
</html>
あと、設定用のyaml
aws.yaml
asoid : xxxxxxxxx
devkey : 1XXXXXXXXXXXXXXXXXXX
locale: jp
kinds:
 - Blended: Blended すべての商品
 - Books: 本
 - Classical: クラシック音楽
 - DVD: DVD
 - Electronics: エレクトロニクス
 - ForeignBooks: 洋書
 - Hobbies: ホビー
 - Kitchen: ホーム&キッチン
 - Music: 音楽
 - MusicTracks: 曲名
 - Software: ソフトウェア
 - SportingGoods: スポーツ
 - Toys: おもちゃ
 - VHS: VHSビデオ
 - Video: DVD&ビデオ
 - VideoGames: ゲーム
 - HealthPersonalCare: ヘルス&ビューティー
これ、AuthorとかAvailabilityなんかのアクセサが最初から決まった型で扱えればコードは2/3くらいになりそう。
いっそBeautifulSoupとかで作った方が作り手側としては納得が行くのかも。
動いてる物は以下
AWS商品検索
追記
パッチを付けるのを忘れてました。
pyaws.diff
--- pyaws/ecs.py.orig   Mon Apr 09 07:38:57 2007
+++ pyaws/ecs.py    Wed May 07 18:12:21 2008
@@ -19,7 +19,8 @@
 
 
 import os, urllib, string, inspect
-from xml.dom import minidom
+from google.appengine.api import urlfetch
+import pxdom
 
 __author__ = "Kun Xi < kunxi@kunxi.org >"
 __version__ = "0.2.0"
@@ -164,10 +165,7 @@
    """Send the query url and return the DOM
    
    Exception is raised if there is errors"""
-   u = urllib.FancyURLopener(HTTP_PROXY)
-   usock = u.open(url)
-   dom = minidom.parse(usock)
-   usock.close()
+   dom = pxdom.parseString(urlfetch.fetch(url).content)
 
    errors = dom.getElementsByTagName('Error')
    if errors:
@@ -282,7 +280,7 @@
    if(plugins == None):
        plugins = {}
 
-   childElements = [e for e in element.childNodes if isinstance(e, minidom.Element)]
+   childElements = [e for e in element.childNodes if isinstance(e, pxdom.Element)]
 
    if childElements:
        for child in childElements:
@@ -291,7 +289,7 @@
                if type(getattr(rc, key)) <> type([]):
                    setattr(rc, key, [getattr(rc, key)])
                setattr(rc, key, getattr(rc, key) + [unmarshal(child, plugins)])
-           elif isinstance(child, minidom.Element):
+           elif isinstance(child, pxdom.Element):
                if plugins.has_key('isPivoted') and plugins['isPivoted'](child.tagName):
                        unmarshal(child, plugins, rc)
                elif plugins.has_key('isBypassed') and plugins['isBypassed'](child.tagName):
@@ -303,7 +301,7 @@
                else:
                    setattr(rc, key, unmarshal(child, plugins))
    else:
-       rc = "".join([e.data for e in element.childNodes if isinstance(e, minidom.Text)])
+       rc = "".join([e.data for e in element.childNodes if isinstance(e, pxdom.Text)])
    return rc
 
 
pxdomはDOM Level 3をpythonで実装しているすばらしいライブラリです。ココから取得して下さい。

2008/03/17

はてな
fukaz55さんの所に置いてあった「Amazon Web Services から情報を取得する blosxom 向けプラグイン」を貰ってきて
AWSWORD:ほにゃらら:
※「ほにゃらら」は英数字もしくは「_」
と指定出来るようにした。
これまでの様に
ASIN:4844322893
と書けば
まるごとPerl! Vol.1 まるごとPerl! Vol.1
小飼 弾
インプレスコミュニケーションズ / ¥ 1,995 (2006-08-24)
 
発送可能時間:


となるし
AWSWORD:perl:
と書けば
初めてのPerl 第5版 初めてのPerl 第5版
Randal L. Schwartz
オライリージャパン / ¥ 3,780 (2009-10-26)
 
発送可能時間:在庫あり。


となる。
結果は一覧されるうちの先頭で表示され、キャッシュは全件「検索語.xml」で保存される。ファイル名の都合で英数字+「_」が制限になってます。
なおblosxom::templateを使ってないのは、mobrowserと干渉しても構わないようにです。
パッチは以下の通り。
--- awsxom.orig Thu Nov 30 22:12:17 2006
+++ awsxom  Tue Mar 18 09:32:22 2008
@@ -57,17 +57,20 @@
 
    # ASIN/ISBNが書かれていたら置き換える
    # テンプレート指定版
-   s/(?:ASIN|ISBN):([A-Z0-9]{10}):(.*?):/to_html($1,$2)/ge;
+   s/(?:ASIN|ISBN):([A-Z0-9]{10}):(.*?):/to_html_asin($1,$2)/ge;
 
    # テンプレート無指定版
-   s/(?:ASIN|ISBN):([A-Z0-9]{10})/to_html($1,$default_template)/ge;
+   s/(?:ASIN|ISBN):([A-Z0-9]{10})/to_html_asin($1,$default_template)/ge;
+
+   # テンプレート無指定版
+   s/(?:AWSWORD):([a-zA-Z0-9_]*?):/to_html_word($1,$default_template)/ge;
 
    return $_;
 }
 
 # ---------------------------------------------------------------------
 # ASINからAmazonのアフィリエイト用HTMLを作成
-sub to_html {
+sub to_html_asin {
    my ($asin, $template) = @_; # ASINとテンプレ名称
    my $cache = "$cachedir/$asin.xml";
    my $url = "http://webservices.amazon.co.jp/onca/xml?Service=AWSECommerceService&SubscriptionId=$devkey&AssociateTag=$asoid&Operation=ItemLookup&ItemId=$asin&ResponseGroup=Medium,Offers";
@@ -90,8 +93,57 @@
    # テンプレートを展開。エラーの場合はエラー文字列を返す
    my $form;
    if (!defined($detail{"ErrorMsg"})) {
-       $form = &$blosxom::template($blosxom::path, $template, 'html');
-       $form =~ s/\$(\w+)/$detail{$1}/ge;
+       #$form = &$blosxom::template($blosxom::path, $template, 'html');
+        my $fh = new FileHandle;
+        if ($fh->open("< $blosxom::datadir/$template.html")) {
+            $form = join '', <$fh>;
+           $form =~ s/\$(\w+)/$detail{$1}/ge;
+            $fh->close();
+        }
+   }
+   else {
+       $form = "<p>" . $detail{"ErrorMsg"} . "</p>";
+   }
+
+   return $form;
+}
+
+# ---------------------------------------------------------------------
+# ASINからAmazonのアフィリエイト用HTMLを作成
+sub to_html_word {
+   my ($word, $template) = @_; # ASINとテンプレ名称
+   my $cache = "$cachedir/$word.xml";
+   my $url = "http://webservices.amazon.co.jp/onca/xml?Service=AWSECommerceService&SubscriptionId=$devkey&AssociateTag=$asoid&Operation=ItemSearch&Keywords=$word&SearchIndex=Books&ResponseGroup=Medium,Offers";
+   my $outfile = "$cachedir/$word.html";
+
+   # 取り込み直す必要はあるか?
+   if (!(-e $cache) || (-M $cache > ($EXPIRE / 24))) {
+   # AWSから情報を取得してキャッシュファイルに保存
+       # UserAgent初期化
+       my $ua = new LWP::UserAgent;
+       $ua->agent($ua_name);
+       $ua->timeout(60);
+       my $rtn = $ua->mirror($url, $cache);
+   }
+
+   # キャッシュからXMLを読み込んで解析
+   my $content = getFile($cache);
+    $content =~ s!.*?(<Item>.*?</Item>).*!$1!is;
+   my $asin = "";
+    $asin = $1 if ($content =~ /<ASIN>([^<]*)<\/ASIN>/);
+    return "" if !$asin;
+   my %detail = parseXML($content, $asin);
+
+   # テンプレートを展開。エラーの場合はエラー文字列を返す
+   my $form;
+   if (!defined($detail{"ErrorMsg"})) {
+       #$form = &$blosxom::template($blosxom::path, $template, 'html');
+        my $fh = new FileHandle;
+        if ($fh->open("< $blosxom::datadir/$template.html")) {
+            $form = join '', <$fh>;
+           $form =~ s/\$(\w+)/$detail{$1}/ge;
+            $fh->close();
+        }
    }
    else {
        $form = "<p>" . $detail{"ErrorMsg"} . "</p>";
ちなみに、先日図書館で見つけた本で面白いの見つけた。
子供向け絵本らしいが、ちょっと笑った。
サトシくんとめんたくん (cub label) サトシくんとめんたくん (cub label)
デハラ ユキノリ
長崎出版 / ¥ 1,575 (2007-08)
 
発送可能時間:在庫あり。