GoogleAppEngine再入門(4) -RSS Readerを作る-
前回:GoogleAppEngine再入門(3) -Datastore API 2- - souta-bot log
日経ソフトウェア200901号のPart3に習い簡易RSS Readerを作る!
- feedparser.py(http://code.google.com/p/feedparser/downloads/list)を使用
- RSSアイコンをここから入手
ディレクトリ構成はこんな感じにする
-hoge-reader/ |-main.py(プログラム本体) |-app.yaml |-feedparser.py |-templates.py(テンプレートファイル) |-index.yaml(データストアのインデックス情報) |-static/ |-rss-big.png |-rss-small.png |-style.css
簡易テンプレート
top_html ="""<html> <head> <title>Read it!</title> <link rel="stylesheet" type="text/css" href="/static/style.css"> </head> <body> <span class="greeting">%(greeting)s</span> <img src="/static/rss-big.png" width="28" height="28" alt="Readit"> <span class="app-title">Read it!</span><br/> <span class="error">%(error)s</span> <br clear="all"/> <form method="POST" action="/"> Type the feed URL and Read it! <input type="text" name="feed_url" size="40"> <input type="submit" name="readit" value="Read it!"> </form> %(feed_list)s </body> </html> """ read_html ="""<html> <head> <title>Read it!</title> <link rel="stylesheet" type="text/css" href="/static/style.css"> </head> <body> <span class="greeting"><a href="/?">Go to Top.</a></span> <img src="/static/rss-big.png" width="28" height="28" alt="Read it!"> <span class="app-title">Read it!</span><br/> <span>Now reading %(feed_url)s.</span><br/> <br clear="all"/> %(contents)s </body> </html> """ feed_list = """ <div class="item"> <a href="%(feed_url)s">%(feed_name)s</a> <a href="/view_feed?key=%(key)s">Read it!</a> </div> """ contents = """ <div class="item"> <a href="%(link)s">%(title)s</a> </div> """
CSSファイル
span.greeting { float: right; } span.app-title { font-size: 14pt; font-weight: bold; } .error { color: red; } div.item { background-image:URL("/static/rss-small.png"); background-repeat: no-repeat; padding-left: 20pt; }
scriptを変えるだけだったapp.yamlもほんのちょっと複雑に
pplication: hoge-reader version: 1 runtime: python api_version: 1 handlers: - url: /static static_dir: static - url: /.* script: main.py
プログラム本体
# -*- coding: utf-8 -*- from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.ext import db, webapp #ユーザ認証を提供する「Users API」 from google.appengine.api import users #取得データをメモリにキャッシュする「Memcache API」 from google.appengine.api import memcache import logging, cgi import templates class Feed(db.Model): name = db.StringProperty() url = db.URLProperty() feed_url = db.URLProperty() #認証時に取得したユーザ名を格納 owner = db.UserProperty() created = db.DateTimeProperty(auto_now_add=True) def parse_feed(feed_url): import feedparser from google.appengine.api import urlfetch #Responseオブジェクトを取得 result = urlfetch.fetch(feed_url) if result.status_code == 200: d = feedparser.parse(result.content) else: raise Exception("Can not retrieve given URL.") #RSSの形式が規格外の場合(bozo=まぬけ) if d.bozo == 1: raise Exception("Can not parse given URL.") return d class Readit(webapp.RequestHandler): def get(self): #ログイン中のユーザ取得 user = users.get_current_user() error = self.request.get("error") #ログイン中なら if user: greeting = ("Welcome, %s! (<a href=\"%s\">sign out</a>)" % (user.nickname(), users.create_logout_url("/"))) query = Feed.all().filter("owner = ", user).order("-created") if query.count() > 0: feed_list = "You have read these items before<br />" else: feed_list = "You have not read anything yet." #フィードを列挙 for feed in query: feed_list += templates.feed_list % dict( feed_url=cgi.escape(feed.url), feed_name=cgi.escape(feed.name), key=feed.key()) else: greeting = ("<a href=\"%s\">Sign in or register</a>." % users.create_login_url("/")) feed_list = "" self.response.out.write(templates.top_html % dict(greeting=greeting, feed_list=feed_list, error=error)) def post(self): feed_url = self.request.get("feed_url") user = users.get_current_user() try: d = parse_feed(feed_url) except Exception, e: logging.error(e) return self.redirect("/?error=Invalid%20URL") feed = Feed(name=d.feed.title, url=d.feed.link, feed_url=feed_url, owner=user) feed.put() #60秒間メモリにキャッシュ→データストアにアクセスする必要なし #しかしキャッシュできるのは1Mバイトまでだからよく考えて #ここではキーにentityのキー(データストア登録時に作成される)を使用した memcache.add(str(feed.key()), d, time=60) self.redirect("/view_feed?key=%s" % feed.key()) class ViewFeed(webapp.RequestHandler): def get(self): key = self.request.get("key") feed = Feed.get(key) d = memcache.get(key) if d is None: try: d = parse_feed(feed.feed_url) except Exception, e: logging.error(e) return self.redirect("/?error=Invalid%20URL") memcache.add(key, d, time=60) contents = "" for entry in d.entries: contents += templates.contents % dict(link=cgi.escape(entry.link), title=cgi.escape(entry.title)) self.response.out.write( templates.read_html % dict(feed_url=cgi.escape(feed.feed_url), contents=contents)) application = webapp.WSGIApplication([('/', Readit), ('/view_feed', ViewFeed)], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()
実行結果
開発環境用のログインページ?
ログイン完了!
FeedのURLを登録すると
TOPページには登録したFeedが
どんどん追加
デプロイしてもちゃんと使えるし(http://hoge-reader.appspot.com/)、もうLDRいらないね(嘘)
これで特集は終了。ページ数が少ないせいか少し説明足らずな部分もありましたが良記事でした。