2011年05月25日

GAE+Pythonの標準モジュールだけでXML解析。簡単なRSSリーダーを作ってみる。

今回は GAE for Python でサードパーティモジュールを一切使わずにXMLをパースする方法を紹介する。

折角なので簡単なRSSリーダーでも作ってみようか。

※その前に、、、
ここでの説明は GAE for Python な開発の基本が分かっていることが前提になっているので、もしよく分かってないならこちらで予習しておこう。

では始めようか。
まず、いつもの helloworld.py を以下のように編集する。
# -*- coding: utf-8 -*-
import cgi
import os

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
from xml.dom import minidom


class MainHandler(webapp.RequestHandler):
def get(self):
url = 'http://sunabako.sblo.jp/index.rdf'
result = urlfetch.fetch(url)
dom = minidom.parseString(result.content)

# ブログ題名取得
blogTitle = dom.getElementsByTagName('channel')[0].getElementsByTagName('title')[0].firstChild.data

# エントリ情報(エントリ題名、URL、作成日の連想配列)のリスト作成
elems = dom.getElementsByTagName('item')
entries = []
for elem in elems:
entry = {}
entry['title'] = elem.getElementsByTagName('title')[0].firstChild.data
entry['link'] = elem.getElementsByTagName('link')[0].firstChild.data
entry['date'] = elem.getElementsByTagName('dc:date')[0].firstChild.data
entries.append(entry)

template_values = {'blogTitle':blogTitle, 'entries':entries}
path = os.path.join(os.path.dirname(__file__), 'rss_reader.html')
self.response.out.write(template.render(path, template_values))
dom.unlink()

application = webapp.WSGIApplication([('/', MainHandler)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()


そしてデザインHTMLのテンプレートは以下の通りだ。
rss_reader.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{{ blogTitle }}のRSS</title>
</head>
<body>
<ul>
{% for entry in entries %}
<li>{{ entry.date }} <a href="{{ entry.link }}" target="_blank">{{ entry.title }}</a></li>
{% endfor %}
</ul>
</body>
</html>


今回のポイントは、以下の通り。
  1. google.appengine.api.urlfetch でRSSのXMLデータ取得
  2. 取得したXMLを xml.dom.minidom で解析
  3. getElementsByTagName() で該当タグのリスト取得


今回利用したXML解析用の標準モジュール xml.dom.minidom はI/FがJavascriptに似ていて直感的に理解しやすく、尚且つ軽いので、ちょっとしたXML解析には適していると思う。

サードパーティ製モジュールを使えば、ソースコードをもっとスッキリさせることも可能なんだけど、まずは標準モジュールを使ってやる方法を理解しておくことも大切だよね。

ここはひとつポチっとよろしく。
人気ブログランキングへ


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 48590



posted by 寄り道退屈男 at 16:52 | Comment(0) | TrackBack(0) | GAE for Python

2011年05月24日

GAE+Pythonでテンプレートの共通化。

以前に GAE for Python の webapp フレームワークでテンプレートエンジンを使ったデザインの外部化を行ったわけだが、今度は更にデザインのメンテナンス性を高めるためにテンプレートの共通化を行ってみる。

まず、各画面共通のテンプレートを作成する。
ファイル名は base.html とした。
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
<br/>
<a href="{% block linkURL %}{% endblock %}">{% block linkName %}{% endblock %}</a>
</body>
</html>


次に、上記の共通テンプレートへ挿し込む画面(というか文字列)を定義する。
ここではそれぞれ page1.htmlpage2.html とした。それぞれのページで共通テンプレートを適用してみるということだ。

page1.html
{% extends "base.html" %}

# タイトル部分
{% block title %} Page1 - Helloworld App {% endblock %}

# コンテンツ部分
{% block content %}
This is Page1.
{% endblock %}

# リンク先URL
{% block linkURL %}/page2{% endblock %}
# リンク先名
{% block linkName %}page2へ{% endblock %}


page2.html
{% extends "base.html" %}

# タイトル部分
{% block title %} Page2 - Helloworld App {% endblock %}

# コンテンツ部分
{% block content %}
This is Page2.
{% endblock %}

# リンク先URL
{% block linkURL %}/page1{% endblock %}
# リンク先名
{% block linkName %}page1へ{% endblock %}


重要なのは先頭行の {% extends "base.html" %} という部分だ。これは共通テンプレート "base.html" を読み込むための記述である。

で、お馴染みの helloworld.py を以下のように編集する。
# -*- coding: utf-8 -*-
import cgi
import os

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


class Page1(webapp.RequestHandler):
def get(self):
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'page1.html')
self.response.out.write(template.render(path, template_values))

class Page2(webapp.RequestHandler):
def get(self):
template_values = {}
path = os.path.join(os.path.dirname(__file__), 'page2.html')
self.response.out.write(template.render(path, template_values))

application = webapp.WSGIApplication(
[('/page1', Page1),
('/page2', Page2)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()


開発用サーバを起動し、 http://localhost:8080/page1http://localhost:8080/page2 へアクセスしてみよう。
それぞれのページ(page1.html, page2.html)が同じテンプレート(base.html)を使い回していることが分かるはずだ。

これで画面のメンテナンス性がグッと向上するわけである。


ここはひとつポチっとよろしく。
人気ブログランキングへ


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 48590



posted by 寄り道退屈男 at 23:45 | Comment(0) | TrackBack(0) | GAE for Python

GAEアプリをアップロードする方法。

前回はwebappフレームワークのテンプレートエンジンを使ってデザインを外部化する方法を紹介した。
今回はいよいよ作成したアプリケーションのアップロード方法を紹介する。

GAE では、アプリの作成と管理を管理コンソールから行う。アプリの ID を登録後、SDK で提供される appcfg.py というコマンドラインツールを使用して、アプリをウェブサイトへアップロードする。

その前に一つだけ注意がある。
GAE にアップロードしたアプリを削除する手段は今のところ提供されていないということだ。1 つの Google アカウントで、最大 10 個のアプリ ID を登録できるのだが、この内 1 つをこのテスト用アプリで消費したくない場合は、この記事は読むだけにし、最初のアプリをアップロードする準備ができたときに参照して欲しい。


■アプリの登録

まず以下の URL にアクセスし、自分のGoogle アカウントで GAE にログインする。Google アカウントを持っていない場合、メール アドレスとパスワードを使って Google アカウントを作成。

http://appengine.google.com/

新しいアプリを作成するには、[Create an Application] ボタンをクリックする。アプリの一意名であるアプリケーション ID を、説明に沿って登録しよう。無償で利用できる appspot.com のドメイン名を選択すると、アプリケーションの URL は http://作成したアプリのID.appspot.com/ となる。アプリのトップレベル ドメイン名は、有償で購入するか、自分で既に登録しているものを使用することができる。

app.yaml ファイルを編集し、application: 設定の値を、helloworld から登録したアプリケーション ID に変更する。


■アプリのアップロード

次のコマンドを実行してアプリをアップロードする。
appcfg.py update helloworld/

言うまでもないが、helloworld/ の部分は自分が作ったプロジェクトフォルダ名に変更すること。

上記コマンドを実行すると、ユーザIDとパスワードを聞かれるので、それぞれ入力する。

以上でアップロードが完了である。

http://作成したアプリのID.appspot.com/ へアクセスし、成功したかを確認しよう。


GAE for Python の基本は以上で全てだ。
まだ理解が足りない場合は、もう一度最初に戻ってじっくり勉強してみてくれ。

これ以降は少しマニアックなテクニックとかを紹介していくつもりだ。


ここはひとつポチっとよろしく。
人気ブログランキングへ


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 48590



posted by 寄り道退屈男 at 16:31 | Comment(0) | TrackBack(0) | GAE for Python

2011年05月23日

GAE+Pythonでテンプレートエンジンを使ったデザインの外部化と静的ファイルの扱い方。

前回はデータストアの操作を説明した。
今回はテンプレートを使用してHTMLやCSSなどのデザイン部分を外部化する方法と静的ファイルの扱い方を説明する。

GAE for Python は有名所のPython用テンプレートシステム(例えば、EZT、Cheetah、ClearSilver、Quixote、Django 等)が、ほぼどれでも使える。とは言え、webapp フレームワークにはデフォルトで Django のテンプレートエンジンが用意されているのでそれを使うのが手っ取り早い。

というわけで、早速その使い方を説明する。


■HTMLを外部化

まずは、いつもの helloworld.py を以下のソースコードに書き換えて見てよう。
# -*- coding: utf-8 -*-
import cgi
import os

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.webapp import template
from google.appengine.ext import db

class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)

class MainPage(webapp.RequestHandler):
def get(self):
greetings_query = Greeting.all().order('-date')
greetings = greetings_query.fetch(10)

if users.get_current_user():
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
else:
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'

template_values = {
'greetings': greetings,
'url': url,
'url_linktext': url_linktext,
}

path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, template_values))

class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()

if users.get_current_user():
greeting.author = users.get_current_user()

greeting.content = self.request.get('content')
greeting.put()
self.redirect('/')

application = webapp.WSGIApplication(
[('/', MainPage),
('/sign', Guestbook)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()


次に、helloworld フォルダに新規で index.html というファイルを作り、以下のようにDjangoテンプレートの作法に則って編集する。詳細についてはDjango組み込みタグ・フィルタリファレンスをチェックすべし。
<html>
<body>
{% for greeting in greetings %}
{% if greeting.author %}
<b>{{ greeting.author.nickname }}</b> wrote:
{% else %}
An anonymous person wrote:
{% endif %}
<blockquote>{{ greeting.content|escape }}</blockquote>
{% endfor %}

<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>

<a href="{{ url }}">{{ url_linktext }}</a>

</body>
</html>


さて、ここまでできたら開発用サーバを起動し、 http://localhost:8080 へアクセスしてみよう。

アプリ画面の一番下に Login というリンクが作られた筈だ。
このリンクをクリックするとダミーのログイン画面が出現するので、そこで適当なメールアドレスを入力するとダミーログイン状態になる。そして、メイン画面でメッセージを投稿するたびに自分のメアドも登録&表示されるようになる。


では上のソースコードから重要な箇所を抜き出して説明してみよう。

まず、テンプレートエンジンを利用するために以下のように関連モジュールを読み込んでいる。
import os
from google.appengine.ext.webapp import template


そして以下のようにしてテンプレートエンジンを扱う。
template_values = {
'greetings': greetings,
'url': url,
'url_linktext': url_linktext,
}

path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, template_values))

直感的に分かる人もいるだろうが一応説明しておくと、template_values = {...} という箇所でテンプレート(index.html)で使用されているキー( {{ hoge }} のように記述されている部分)とそこへ格納する値を紐付けており、path = os.path.join(os.path.dirname(__file__), 'index.html')で index.html の絶対パスを取得し、最後に template.render(path, template_values) で index.html へテンプレートを適用&レンダリングされたHTMLテキストを返している。

なお、テンプレートを共通化してメンテナンス性を更に高めたい場合はこちらを参照のこと。


■CSSや画像も外部化

次は、スタイルシートや画像などの静的ファイルを扱えるようにしてみよう。
まず、app.yamlを以下のように書き換える。
application: helloworld
version: 1
runtime: python
api_version: 1

handlers:
- url: /stylesheets
static_dir: stylesheets

- url: /.*
script: helloworld.py

そして、helloworld/stylesheets フォルダを作成して、そこへ main.css というファイルを作成し、以下のように書く。
body {
font-family: Verdana, Helvetica, sans-serif;
background-color: #DDDDDD;
}


次に先程の index.html にこのスタイルシートを読み込ませるために<html>直下に以下のような記述を追加する。
  <head>
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
</head>


これでページをリロードしてみればスタイルシートが適用されている筈だ。

app.yaml に追加された、
- url: /stylesheets
static_dir: stylesheets

という部分は、/stylesheetsというURLへのアクセスがstylesheetsというフォルダ配下にある静的ファイルへのアクセスであることを表している。

画像の静的ファイルを使いたい場合も上と同様に、app.yamlhandlers:の下へ
- url: /images
static_dir: images

を追加し、helloworld/imagesフォルダを作り、そこへ適当な画像ファイル(例えば hoge.jpg)を保管すれば、index.html内で以下のようにして画像が扱えるようになる。
<img src="/images/hoge.jpg" />


要するにCSSだろうが画像だろうが、静的ファイルを扱いたい場合は app.yaml を弄って明示的にファイルの場所を指定すればいいということだ。


GAE for Pythonにおけるデザインの外部ファイル化は以上である。

次回はGAEアプリのアップロードを説明する。いよいよあなたのアプリが世界へお披露目されるというわけだ。


(続く)



posted by 寄り道退屈男 at 17:48 | Comment(0) | TrackBack(0) | GAE for Python

2011年05月22日

GAE+Pythonでデータストアを操作。

前回は webapp フレームワークをフォーム操作を紹介した。
今回はデータストア(Datastore)の使い方を紹介する。
データストアとは、言うなればGAEにおけるデータベースである。GAEアプリにおけるデータの永続化はこいつを使って行う。

早速ソースコードを書いてみよう。
お馴染みの helloworld.py を以下のように編集してみてくれ。
# -*- coding: utf-8 -*-
import cgi

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db

class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)

class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write('<html><body>')

greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")

for greeting in greetings:
if greeting.author:
self.response.out.write('<b>%s</b> wrote:' % greeting.author.nickname())
else:
self.response.out.write('An anonymous person wrote:')
self.response.out.write('<blockquote>%s</blockquote>' %
cgi.escape(greeting.content))

# Write the submission form and the footer of the page
self.response.out.write("""
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
</body>
</html>""")

class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()

if users.get_current_user():
greeting.author = users.get_current_user()

greeting.content = self.request.get('content')
greeting.put()
self.redirect('/')

application = webapp.WSGIApplication(
[('/', MainPage),
('/sign', Guestbook)],
debug=True)

def main():
run_wsgi_app(application)

if __name__ == "__main__":
main()

そして、いつものように開発用Webサーバを起動し、 http://localhost:8080 へアクセスしてみよう。

画面にはテキストエリアと「Sign Guestbook」ボタンが表示されていて、そこへ何か適当に入力すると、書き込みがどんどん追記されていくという、いわゆる掲示板アプリだ。

では、ソースコードを説明しようか。

まず、データモデリングAPI(データストアを利用するためのAPI)を使うためのモジュールをインポートする。
from google.appengine.ext import db


次に、このソースコードで定義されている三つのクラスに注目する。

一つ目のクラスは、Greetingクラスだ。こいつはdb.Modelというのを継承していて、データモデルを表している。
class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)

このデータモデルでは、ユーザ情報オブジェクトとしてのauthor、文字列オブジェクトとしてのcontent、日付オブジェクトとしてのdate という三つのプロパティを宣言している。
contentプロパティは、multiline=Trueが設定されているため、改行も入力可能となっている。また、dateプロパティには、auto_now_add=Trueという設定がなされているため、入力が空の場合は自動的に現在日付(now)がセットされる。

二つ目のクラスは、MainPageクラスだ。このクラスは、データストアへアクセスして永続化されたデータを取得し、それらを画面へ表示する。重要なのは以下の部分だ。

greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")

db.GqlQueryメソッドにGQLというSQLに似たクエリ文字列を渡し、Greetingオブジェクトのリストを取得している。
これは以下の記述でも代用できる。
    greetings = Greeting.gql("ORDER BY date DESC LIMIT 10")

以下のようにしてパラメータを渡すことも出来る。以下は全て同じ意味だ。
      greetings = Greeting.gql("WHERE author = :1 ORDER BY date DESC",
users.get_current_user())

      greetings = Greeting.gql("WHERE author = :author ORDER BY date DESC",
author=users.get_current_user())


      greetings = Greeting.all()
greetings.filter("author =", users.get_current_user())
greetings.order("-date")

データストアに関してもっと詳しく知りたいならこちらを参照のこと。

三つ目のクラスは、Guestbookクラスだ。このクラスは「Sign Guestbook」ボタンが押されたときに、入力された内容をGreetingデータモデルへ格納し、データストアへ保存。最後に/へリダイレクトしている。重要な部分は以下である。
    greeting = Greeting()

if users.get_current_user():
greeting.author = users.get_current_user()

greeting.content = self.request.get('content')
greeting.put()

greeting = Greeting()Greetingデータモデルのインスタンスを生成し、そのauthorプロパティへGoogleインフラから取得したユーザ情報をセット、contentプロパティにはpost送信されてきた'content'データをセットし、最後にgreeting.put()でデータストアへ永続化している。

三つのクラスの説明は以上だ。

もう説明する必要もないと思うが、48-51行目ではURLと呼び出されるクラスを紐付けている。

ソースコードの説明は以上である。
GAE for Python のデータモデリングAPIはシンプルでいいね。

あ、一つだけ説明するのを忘れていた。
開発サーバーのデータストアをクリアする方法だ。
開発用Webサーバは、アプリをテストするために一時ファイルを使用したローカル用のデータストアを備えており、この一時ファイルが存在するうちはデータは永続化されている。これを開発用Webサーバ起動時に明示的に削除するには、--clear_datastore オプションを使用する。例えば、今回の helloworld アプリをデータストアを削除しつつ起動するには、以下のようにすればいい。
dev_appserver.py --clear_datastore helloworld/



さて、徐々に実用的なアプリ開発に近づいてきているわけだが、これまでのようにHTMLをソースコード内に書くのはどうもスマートではない。というわけで、次回はHTMLテンプレートを外部化する方法を説明する。


(続く)



ここはひとつポチっとよろしく。
人気ブログランキングへ


プログラミング Google App Engine
Dan Sanderson
オライリージャパン
売り上げランキング: 48590



posted by 寄り道退屈男 at 21:40 | Comment(0) | TrackBack(0) | GAE for Python