風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

というわけで、Google App EngineにてFeedを配信する方法のメモ

抜け道探して遠まわり

きまぐれびゅーの各ページでFeed配信したいな

でも、いちいちRSSとかAtomのXML構造考えてテンプレートとか作るのって、すごく面倒じゃない?

きっと探せば、PythonのFeed作成用ライブラリとかあるさ。

うーん、たとえばPyRSS2Genとか見つけたけど、UTF-8対応じゃないみたいだし……いまいち使い辛そう。

やっぱ、地道にぽちぽちやるしかないのかなぁ……。

まてよ、そういえば、Google App Engineが標準で採用しているdjangoって、いろんな機能ついてるんじゃないっけ?simplejsonとかもあったし……。

お、C:\Program Files\Google\google_appengine\lib\django\django\utils に feedgenerator.py とかある、それっぽくない?

検索してみたら……おおっ、どうやらそのものみたい→配信フィードフレームワーク ― Django v1.0 documentation


と、実際のコード組むより、探す方に圧倒的に時間を食ってしまいました。

そのFeed配信用のコードをさらしてみる

好きな本のAmazonカスタマーレビューをFeedで購読! - 風柳メモ

で紹介したFeed配信用のコードです。

# -*- coding: utf-8 -*-

"""
■ Amazon カスタマーレビュー配信 Feed 作成

"""
import sys,os,cgi,re,urllib,logging,cgi
import datetime
import wsgiref.handlers

from django.utils import feedgenerator

from google.appengine.ext import webapp
from google.appengine.ext.webapp import template

from awslib import AwsSearch

SITE_BASE=u'http://honnomemo.appspot.com/amazonreview?asin='

CONTENT_TYPE_XML='text/xml'
CONTENT_TYPE_XML_RSP=CONTENT_TYPE_XML+'; charset=utf-8'
CONTENT_TYPE_HTML='text/plain'

DEFAULT_CACHE_TIMEOUT=900

class showFeed(webapp.RequestHandler):
  def get(self):
    kind=self.request.get('kind','rss')
    if kind=='atom':
      fncfeed=feedgenerator.Atom1Feed
    elif kind=='rss':
      fncfeed=feedgenerator.Rss201rev2Feed
    else:
      self.error(400)
      return
    
    asin=urllib.unquote(re.compile(r'^.*/feed/(.*)/.*$').sub(r'\1',self.request.url))
    awssrch=AwsSearch()
    info=awssrch.getSingleInfo(asin,getreviewlink=True,response=['Small','Reviews'])
    if not info or not info.get('Item') or str(info.Item[0].ASIN)!=asin:
      self.error(404)
      return
    
    item=info.Item[0]
    attrib=item.ItemAttributes
    crev=item.CustomerReviews
    
    sitelink=SITE_BASE+asin
    
    description=u'きまぐれびゅー:『'+attrib.Title+u'』のAmazonカスタマーレビュー'
    avestr=crev.get('AverageRating',None)
    if avestr:
      description+=u'(おすすめ度:星%s)' % (avestr)
    
    feed=fncfeed(
           title=attrib.Title
         , link=sitelink
         , description=description
         , language=u'ja'
         )
    for review in crev.Review:
      reviewer=review.Reviewer
      author=reviewer.get('Nickname',None)
      cid=review.get('CustomerId',None)
      if not author: author=reviewer.get('Name',None)
      if not author: author=cid if cid else u'(非公開)'
      yyyy,mm,dd=review.Date.split('-')
      dobj=datetime.datetime(int(yyyy),int(mm),int(dd),0,0)
      dobj-=datetime.timedelta(hours=9)
      link=review.get('PermaLink',sitelink)
      description=u'<p><img src="http://g-ecx.images-amazon.com/images/G/09/x-locale/common/customer-reviews/stars-%s-0._V45731007_.gif" alt="星%sつ" border="0"/></p>' % (review.Rating,review.Rating)
      description+=review.Content
      feed.add_item(
             title=review.Summary
           , link=link
           , description=description
           , author_name=author if kind=='atom' else None
           , author_email=author if kind=='rss' else None # RSS 2.0 仕様では author 要素は e-mail アドレス
           , author_link=u'http://www.amazon.co.jp/gp/pdp/profile/'+cid+u'/ref=cm_cr_dp_pdp' if cid else ''
           , pubdate=dobj
           )
    self.response.headers['Content-Type']=CONTENT_TYPE_XML_RSP
    self.response.out.write(feed.writeString('utf-8'))

def main():
  application = webapp.WSGIApplication([
    ('/amazonreview/feed/.*', showFeed)
  ],debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
  main()

配信フィードフレームワーク ― Django v1.0 documentation高水準フレームワークの方はなんだか難しくてよくわからなかったので(苦笑)、低水準フレームワークの基本機能のみ使ってます。

概要

Feed作成に関するところだけ抜粋すると。

from django.utils import feedgenerator

でfeedgeneratorをimport し、例えばRSS 2.0なら

feed=feedgenerator.Rss201rev2Feed(
       title=[サイトタイトル(必須)]
     , link=[サイトリンク(必須)]
     , description=[サイト概要(必須)]
     , language=[言語(u'ja'とか適当にセットしたけど、あってるのか?u'ja-jp'とか?)
     )

で大元を作り(Rss201rev2Feedの代わりに、RSS 0.91ならRssUserland091Feed、Atom 1.0ならAtom1Feedになる)

feed.add_item(
       title=[アイテムのタイトル(必須)]
     , link=[アイテムのリンク(必須)]
     , description=[アイテムの概要(必須)]
     , author_email=[アイテムの著者名(RSS 2.0の場合:仕様上はメールアドレスだが、そうでなくても受付はする)]
     , author_name=[アイテムの著者名(Atomの場合)]
     , author_link=[アイテムの著者のリンク]
     , pubdate=[日付:Python datetime 型オブジェクト(datetime.datetime()とかdatetime.datetime.now()とかの戻り値)]
     )

で、各アイテムを追加していく感じ(その他のオプションは上で紹介したサイトとかで随時調べてください)。
全て追加しおわったら、

result=feed.writeString('utf-8')

で文字列(上記例ではUTF-8としてencode済みの文字列)として取得できるので、後は適当に

self.response.headers['Content-Type']='text/xml; charset=utf-8'
self.response.out.write(result)

のようにして出力してやればよいだけ、みたいです。かなりお手軽だと思いました。

ちなみに

from awslib import AwsSearch

として組込んでいるのは、先日公開したAmazon Web Service用のWrapperです。