GAE/PでBeautifulSoupを動かす際のはまりどころとか

2012-09-30   treby   技術メモ  , このエントリーをはてなブックマークに追加

ご存知の方もいらっしゃるかもしれませんが、先日福岡で明星和楽というアジアのテクノロジーとクリエイティブに関わる人々が福岡に集まるイベントが開催されました。それで明星和楽内ワークショップの中でGoogle App Engine トークセッションというのが行われたんですね。私自身はこのセッションに参加できなかったのですが(Fukuoka NFC Hack 4当日でした)、その前日@kenz_firespeed氏のお誘いでトークセッション登壇者の松尾氏を交えたコードラボが行われたのです。その場で松尾氏に直にGAEとやらを教えていただきまして、GAEいいなーと使っているのが最近の私です。まあ、Pythonわかんないので雰囲気で書いているんですけどね。

ちなみに明星和楽に関しましては私も前日と二日目はボランティアスタッフとして参加していました。ちょっとだけお疲れオレ。もうひと月前の話だけれど。

導入はサクサク・日本語も楽勝

基本的にはApp Engine Python2.7 コードラボを読まれると導入までさっくりできます。この手軽さ、いい感じですね。

Hello, World!!

BeautifulSoupもなんか動いた

今やろうとしていることは、ATNDのようなイベント管理システムから特定イベントの参加者一覧を取得することです。はじめはただ単にAPI叩けばいいのかなーと思っていたのですが、ものによってはそのためのAPIが提供されてないようです。例えばconnpasseventATNDではユーザ名から参加イベントを検索するAPIは用意されていますが、イベント自体の参加者を取得するAPIは提供されていません。

そこでスクレイピングというテクニックを使って、Webページから必要な情報を取得することを考えました。いろいろ調べた結果PythonにおいてはBeautifulSoupというライブラリを使うと良いらしい、ということでこちらの記事などを参考にしながら動かしてみると、コンソール上では上手く動いてくれました。

GAE上で解析が上手くいかない

が、いざそれをGAE用のファイルとして形を整えてみると今度は何やらエラーが出てきます。

Internal Server Error

HTMLParseError: malformed start tag, at line ほにゃらら, column ほにゃらら

やっていることは同じなのに変な感じです。

html5libは置いておくだけでよい

どうやらBeuatifulSoupは内部で別のParserを使っているみたいです。で、Ver.4の場合は最初Python標準のHTMLParserで試してみて解析できなかった場合は他のParserに切り替える的なことを行っているようです。上手く動いたコンソール向けプログラムをGAE用に移植すると動かなかったのは恐らく、代替のParserが見つからなかったからだと思われます。

結果としては、html5libというのを新たに入れてあげればこの問題は解決しました。導入の仕方も単純でただ解答してきたhtml5libディレクトリをそのままGAEのディレクトリにコピーするだけ。BeautifulSoupの導入と同じ感じです。

文字コードは取り込むときに指定すべし

あと細々としたところで気になるのが文字化けの問題です。

soup = BeautifulSoup(urllib.urlopen("").read(), fromEncoding='utf-8')

これは上のように、BeautifulSoupにページを読ませる時に指定してあげるといいみたいです。

コード

で、下がそのコードです。

# -*- encoding:utf-8 -*-
# -*- encoding:utf-8 -*-
import webapp2
import urllib
from bs4 import BeautifulSoup

class MainHandler(webapp2.RequestHandler):
    def get(self):
        soup = BeautifulSoup(urllib.urlopen("http://connpass.com/event/1059/").read(), fromEncoding='utf-8')
        participants = []

        self.response.headers['Content-Type'] = 'text/plain; charset=utf-8'
            
        self.response.out.write(soup.prettify())
        
app = webapp2.WSGIApplication([
  ('/', MainHandler)
], debug=True)