2009年3月25日水曜日

Google App Engine: Googleはてブを作ってみた

このブログ記事をはてなブックマークに追加

Googleの検索結果にリンク付きのはてなブックマーク件数を表示できれば便利だと思い、Google App Engineを使ってウェブアプリケーションとして実装してみた。

最初、はてなブックマーク件数取得APIを使ってみたが、非常に遅い。これはXML-RPC APIによる実装だが、件数を取得するまでブラウザに表示できないのでかなり待たされる。酷いときにはGoogle App Engineの制限によりタイムアウトしてしまうこともあった。仕方がないので素直にhttp://b.hatena.ne.jp/entry/を使って画像表示を行うことにした。


今時のブラウザは遅延読み込みによる画像表示のため待たされずにサクサク表示できる。


一方、API版は画面に表示されるまでかなり待たされる。

コードについては、あまり時間をかけずにザザッと書いたので結構いい加減かも。日本語処理で少しはまって適当に処置した。最後にソースコードを示しておく。

画像表示版:

class GoogleHatenaImage(webapp.RequestHandler): def __init__(self): self.google_url = "http://www.google.co.jp/" self.google_search_url = "http://www.google.co.jp/search" def get(self): uri_data = urlparse.urlsplit(self.request.uri) baseuri = uri_data[0] + "://" + "".join(uri_data[1:3]) url = self.request.get("content").encode("utf-8") query = self.request.query_string.replace("Shift_JIS", "UTF-8") if not url: result = urlfetch.fetch("%s?%s" % (self.google_search_url, query)) else: result = urlfetch.fetch(query.replace("content=", "")) if result.status_code == 200: content = result.content.decode("cp932") content = re.sub("charset=Shift_JIS", "charset=UTF-8", content) p = re.compile("a href=[\'\"]\/(.+?)[\'\"]") p2 = re.compile("<a href=[\'\"]http(.+?)[\'\"](.+?)\/a>") p3 = re.compile("action=\"\/search") content = p.sub("a href=\"%s?content=%s%s\"" % (baseuri, self.google_url, "\g<1>"), content) content = p2.sub("<a href=\"http\g<1>\"\g<2>/a><a href=\'http://b.hatena.ne.jp/entry/http\g<1>\'><img src=\'http://b.hatena.ne.jp/entry/image/http\g<1>\' style=\'border: none;\'/></a>", content) content = p3.sub("action=\"%s" % baseuri, content) content = content.replace("</head>", "</head><base href=%s />" % self.google_url) self.response.out.write(content)

XML-RPC API版:

class GoogleHatenaXmlrpc(webapp.RequestHandler): def __init__(self): self.s = xmlrpclib.ServerProxy("http://b.hatena.ne.jp/xmlrpc") self.google_url = "http://www.google.co.jp/" self.google_search_url = "http://www.google.co.jp/search" def insert_hatena(self, matchobj): cnt = self.s.bookmark.getCount("http" + matchobj.group(1)).values()[0] if cnt > 0: us = "users" if cnt > 1 else "user" style_format = "style='font-size: 0.9em; text-decoration: underline; white-space: pre; background-color: #fff0f0; font-weight: bold; color: #f06060;'" if cnt >= 10: style_format = "style='font-size: 0.9em; text-decolation: underline; white-space: pre; background-color: #ffcccc; font-weight: bold; color: red;'" return "<a href=\"http%s\"%s/a><a %s href=\'http://b.hatena.ne.jp/entry/http%s\'>%d %s</a>" % (matchobj.group(1), matchobj.group(2), style_format, matchobj.group(1), cnt, us) else: return "<a href=\"http%s\"%s/a>" % (matchobj.group(1), matchobj.group(2)) def get(self): uri_data = urlparse.urlsplit(self.request.uri) baseuri = uri_data[0] + "://" + "".join(uri_data[1:3]) url = self.request.get("content").encode("utf-8") query = self.request.query_string.replace("Shift_JIS", "UTF-8") if not url: result = urlfetch.fetch("%s?%s" % (self.google_search_url, query)) else: result = urlfetch.fetch(query.replace("content=", "")) if result.status_code == 200: content = result.content.decode("cp932") content = re.sub("charset=Shift_JIS", "charset=UTF-8", content) p = re.compile("a href=[\'\"]\/(.+?)[\'\"]") p2 = re.compile("<a href=[\'\"]http(.+?)[\'\"](.+?)\/a>") p3 = re.compile("action=\"\/search") content = p.sub("a href=\"%s?content=%s%s\"" % (baseuri, self.google_url, "\g<1>"), content) content = p2.sub(self.insert_hatena, content) content = p3.sub("action=\"%s" % baseuri, content) content = content.replace("</head>", "</head><base href=%s />" % self.google_url) self.response.out.write(content)

追記(2009/3/26):

日本語処理を若干修正。

5 コメント:

turipat さんのコメント...

ご存じかもしれませんが、同様のことを実現する Greasemonkey もあります。わたしは愛用しています。
http://d.hatena.ne.jp/kusigahama/20051207#p1

nox さんのコメント...

turipatさん、こんにちは。コメントありがとうございます。

GreasemonkeyはFirefoxでの大きなアドバンテージですよね。自分は残念ながらメインブラウザがGoogle Chromeなので、教えて頂いたスクリプトについては存じていましたが、利用できませんでした。Chromiumだったら動くのかな?

それと、今回作ったのは遊びとしてのプログラミングも兼ねているので、使えたとしても作っていたかもしれません。

nox さんのコメント...

API版は遅いと書きましたが、JavaScriptと組み合わせて遅延読み込みにすれば問題なさそうですね。

turipat さんのコメント...

> それと、今回作ったのは遊びとしてのプログラミングも兼ねているので、使えたとしても作っていたかもしれません。

そうですよね。GoogleAppEngineテストの一貫だと思ったので野暮なツッコミだとは感じていたのですが・・・
ご丁寧な返信ありがとうございます。

ブログ愛読させていただいてます。これからも更新楽しみにしています。
失礼します。

nox さんのコメント...

turipatさん、こんばんは。

当ブログを読んでいただいているそうで、ありがとうございます。

コメントで意見などを書いてもらえると、こちらとしても大変有り難いので、もし気が付いたことなどがあれば今後ともコメントしていただけると助かります。