スポンサード リンク

Google App Engine SDK 1.0.2 でクエリ文字列を含む URL のデータが取得できない件(解決済)

注: この問題は、Google App Engine SDK バージョン 1.1.0 で修正済みです。

Google App Engine SDK が 1.0.2 にバージョンアップしたので、さっそくインストールしてみました。念のため、1.0.1 をアンインストールしてから、1.0.2 をインストールしました。

ところが、試しに動かしてみると、検索結果がつねに 0 になったり、商品情報なしになります。そこで、簡略化したスクリプトを使って、ちゃんとデータが取得できているのか確認してみました。

確認用のスクリプト

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

# モジュールのインポート
import wsgiref.handlers
from google.appengine.ext import webapp

# Google App Engine ハンドラ
class Fetch(webapp.RequestHandler):
    def get(self):
        # Amazon ECS の URL
        request_url = 'http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService&AWSAccessKeyId=(以下略)'
        # Google AppEngine urlfetch
        from google.appengine.api import urlfetch
        xml_data = urlfetch.fetch(request_url).content
        # 出力
        self.response.headers['Content-Type'] = 'application/xml'
        self.response.out.write(xml_data)

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

if __name__ == "__main__":
    main()

取得した XML 形式のデータをそのままブラウザに表示するだけのものです(Amazon ECS の URL は省略してありますので、そのままでは動きません)。

結果

すると、

リクエストには、Serviceパラメータが含まれていません。リクエストに Serviceパラメータを追加し、再度リクエストを実行してください。

というエラーメッセージが返ってきました。Amazon.com の場合は、

Your request is missing the Service parameter. Please add the Service parameter to your request and retry.

でした。

Service パラメータは、Amazon ECS の URL の「Service=AWSECommerceService」の部分でちゃんと指定してあります。にもかかわらず、このパラメータが含まれていないということは、「urlfetch.fetch()」に渡した後に URL の文字列が正しく処理されていないのでしょうか。

ちなみに、すべての XML データが取得できないかというとそうでもないようです。Amazon 以外のものもいくつか試してみましたが、たとえば私のブログの RSS(http://feeds.feedburner.com/niiyan)は上のスクリプトで取得することができました。しかし、はてなブックマークの最近の人気エントリー(http://b.hatena.ne.jp/hotentry?mode=rss)はダメでした。

素人考えで「クエリ文字列を含む URL の処理に何か問題があるのだろうか」と思ったりもしましたが、試した数が少なく、また問題箇所を突き止めたわけでもないので何とも言えません。もうちょっと調べて見るつもりではいますが、これ以上は Python に詳しくないと無理かもしれません。

まとめ

とりあえず、私が試した範囲でわかったことや気づいたことを、最後にまとめておきます。

  • Google App Engine の SDK を使わずに、Python だけ(urllib とか)でスクリプトを組むと、ECS のデータは取得できる(スクリプト自体には問題がない?)。
  • Google App Engine の SDK のバージョンを1.0.1 に戻したら、意図したとおりに動作した。
    • 1.0.2 のアンインストールでいくつかファイルが残っていたので、いったん削除してから 1.0.1 を入れ直している。
  • オンラインの Google App Engine に置いてあるものは、今のところ、動作しているらしい。
  • 取得できない URL と取得できる URL がある。

追記(2008-05-12)

Issue 341 - googleappengine - Google Code

Google Code の Issue Tracker に私と同じ症状と思われる報告があがっていました。現時点では、まだコメントなどはついていません。

一方、コメントをいただいた mattn さんによれば、1.0.2 でも問題なく取得出来たそうです。

追記(2008-05-15)

修正パッチ

Issue 341 - googleappengine - Google Code にて、jkester さんがパッチを提供されています。「google_appengine/google/appengine/api/urlfetch_stub.py」に対するパッチです。

urlfetch_stub.py を見てみたところ、70 行目と 118 行目で urlparse を使って、パス(path)やクエリ(query)などを取り出していますが、このあとこの query はどこにも使われていないように見えます。jkester さんのパッチは、この query が空でない場合には、path の後ろに ? と query を追加するというもののようです。

実際にパッチを適用してみたところ、Amazon ECS のようにクエリ文字列を含む URL からデータを取得できるようになりました。とりあえず、ローカルで開発する分にはこれで何とかなりそうです。ただ、オンライン(App Engine)上のアプリが動かなくなるのでは、という不安は残りますが……。

ちなみにパッチの適用の仕方については、patch/diff Tips が参考になります。今回のパッチの場合、そんなに量が多くないので、該当部分を手動で修正してもいいと思います(念のため、オリジナルのバックアップを取っておいたほうがいいでしょう)。

改題

初出時のタイトルは「Google App Engine SDK 1.0.2 で Amazon ECS のデータが取得できない件」でしたが、クエリ文字列を含む URL が関係する問題のようなので、記事タイトルを「Google App Engine SDK 1.0.2 でクエリ文字列を含む URL のデータが取得できない件」に改めました。

追記(2008-05-30)

28 日付けでリリースされたGoogle App Engine SDK バージョン 1.1.0 で、この問題は修正されました。

Issue 341 - googleappengine - Google Code

Comment 13 by ryanb+ap...@google.com, Today (6 hours ago)

hi all! thanks for the report, and sorry for the trouble. this should be fixed in 1.1.0.

Google App Engine: 独自ドメインでの利用

Google Apps を使えば、Google App Engine のアプリを独自ドメインで運用できるらしいので、実際にやってみた。

Google Apps の登録

まず、Google Apps にドメインを登録する。Google Apps でもドメインが取得できるらしいが、今回はすでに使用していた amabiki.com のドメインをそのまま利用することにした。

登録手順は、以下のサイトを参考にした。

Google App Engine を独自ドメインを利用することだけに限定するのなら、「メールの配信設定」や「ウェブの公開設定」は、とくに不要だと思われる。「Add more services(サービスの追加)」からウェブページを追加する必要もない。逆にサブドメインがバッティングしたりしないように気をつける。

Google Apps のコントロールパネルを拡張版に変更

2008-05-04 現在、Google Apps で Google App Engine を利用するには、コントロールパネルの設定を「拡張版 (アメリカ英語のみ)」に変更する必要がある。

ドメインの設定 > 全般 > コントロール パネル > 拡張版 (アメリカ英語のみ) にチェック -> 変更を保存

言語(Language)の設定ではなく、コントロールパネルの設定。「現在のバージョン」を選択していると、Google App Engine 側からドメインを追加する際に Forbidden 403 エラーが出るので注意。

Dashboard > Add more services をクリックした時に、Other services のところに Google App Engine が追加されている。こちらから追加することも可能かもしれない。

アプリの追加

以下のサイトを参考にした。

「Restricted to the following Google Apps domain:」の部分は、アプリを Google Apps で追加したユーザーのみに制限するということだと思うので、その必要がなければデフォルトのままでいいと思われる。

上にも書いたように、Google Apps 側から追加することも可能かもしれない。

サブドメインの設定が必須のようなので、www をサブドメインに設定した。ただし、サブドメインなしでアクセスしても www にリダイレクトされる模様。

というわけで、Google App Engine を利用して作った Amazon 検索サイト。

amabiki - Amazon 検索

Google App Engine SDK: アプリケーションのアップロード

やっと Google App Engine からの招待状が来たので、とりあえずアップロードしてみる。以下のドキュメントやページを参考にした。

debug=False

application = webapp.WSGIApplication(
                                    [('/', MainPage)],
                                    debug=False)

開発中は debug=True にしていたが、運用中にはデバッグ情報を表示する必要はないだろうと思ったので False にしてみた。

2008-05-28 追記: Big Sky :: Google App Engineでアプリを高速化する3つのtips によると、高速化のため(CPU Quota を出さないため)にも False にした方がいいそうです。また「debug=Trueだとスタックトレースに変数の値が表示されてしまい無茶苦茶危険」とのこと。

アップロード

コマンドプロンプトを開いて、

cd "C:\Program Files\Google\google_appengine"
appcfg.py update example/

こんな感じで入力する。1 行目がカレントディレクトリの変更で、2 行目がアップロードのコマンド。example/ はアプリのディレクトリ

favicon.ico の MIME タイプ

favicon.ico の MIME タイプがわからないということでエラーが出た。app.yaml を開いて、favicon のハンドラのところに

mime_type: image/vnd.microsoft.icon

という行を追加。ハイフン(-)ではなく、アンダースコア(_)である点に注意。

favicon の MIME タイプについては、下のページを参考にした。

ただし、Google Apps で独自ドメインでの運用を行う設定を行った場合、favicon は反映されないらしい(2008-05-04 現在)。

2008-05-28 追記: いつ頃からかわかりませんが、こちら側で用意した favicon が表示されるようになりました。

アップロードのやり直し

アップロードが中途半端に終わってしまったので、rollback コマンドでいったん元に戻す。

使い方は基本的に update コマンドと同じ。

appcfg.py rollback example/

詳しくは、Uploading an App - Google App Engine - Google Code を参照。

つづけて update コマンドでアップロード。以上でアプリケーションのアップデートは完了。

アップロードしたアプリ

とりあえず問題なく動いている様子。

Google App Engine SDK: XML データの取得、パース、結果表示まで

ちょうどあまびきを 1 から作り直そうと考えていたところだったので、Google App Engine でやってみることにした。前回、検索フォームの設置まで行ったので、実際に Amazon ECS にリクエストして、返ってきたデータをパース、さらに画像を表示するまでをやってみた。

まず Google App Engine SDK と組み合わせないで、Amazon から XML データを取ってきてパースするというところまでは何とかできた。でも、いざ Google App Engine と組み合わせると動かない。

urllib でエラー

まず、urllib モジュールの urlopen を使って Amazon にリクエストしようとしたら、

AttributeError: 'module' object has no attribute 'unlink'

というエラーが出てうまく行かなかった。urllib2 でもやってみたが、別のエラーが出てうまく行かない。

代わりに Google App Engine の URL Fetch API を使うことにした。

ElementTree でもエラー

Google App Engine には Python 2.5.x が入っているということなので、ElementTree モジュールを使って XML をパースしようと考えた。が、いざ Google App Engine 上(ローカル)で実行してみると、

ImportError: No module named expat; use SimpleXMLTreeBuilder instead

というエラーが出た。

SimpleXMLTreeBulider で検索すると、Big Sky :: Google App EngineでXMLをパースする方法(python版WebService::Simpleを使ってflickr画像検索を作る) に行き当たる。だが私のレベルでは、PyWrapper が一体何で、どうすればいいのかわからず……。

だが PyWrapper のサイト内にある SimpleXMLTreeBuilder.pyElementTree.py を落としてきて使ってみると、うまく XML がパースできるようになった。とりあえずよしとする。

なお、SimpleXMLTreeBuilder の使い方については、上述の Big Sky の記事にある webSimple.py を参考にさせていただいた。感謝。

結果の表示

商品画像

とりあえず画像の表示まで。

関連記事

Google App Engine SDK: 検索フォームを作ってみる

少し戻って Handling Forms With webapp - Google App Engine - Google Code を読みながら、簡単な検索フォームを作ってみる。

基本的には、ほぼ Handling Forms With webapp - Google App Engine - Google Code のまま。ただし、すでにテンプレートの使用を勉強済みだったので、検索結果の表示には別にテンプレートを用意した。

1. result.html の作成

検索結果表示用のテンプレートを作成。result.html とする。

2. cgi モジュールのインポート

import cgi

文字列のエスケープに使用。

3. 検索用のクラスを作成

class Search(webapp.RequestHandler):
    def post(self):
        # クエリを取得してエスケープ
        query = cgi.escape(self.request.get('q'))
        # テンプレートに渡す値を更新
        template_values.update({
            'query': query
        })
        # パスの結合
        path = os.path.join(os.path.dirname(__file__), 'result.html')
        # テンプレートを元に画面に書き出す
        self.response.out.write(template.render(path, template_values))

4. Search ハンドラの URL マッピングを追加

webapp.WSGIApplication に Search ハンドラの URL マッピングを追加:

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

5. 実行

テスト

とりあえず、検索フォームに入力した文字列を取り出して表示するところまで。

関連記事

Page 1 of 2: 1 2