はじめてのGoogle App Engineアプリ/Pythonプログラミング
なので、記念に(笑)ソースもさらしておきます。
# -*- coding: utf-8 -*- """ getTotalHB.py for Google App Engine ■概要 はてなブックマークの被ブックマーク合計数取得API結果をJSONまたは画像で取得 ■使い方 [JSON] http://furyu-tei.appspot.com/getTotalHB?uri=(取得サイトのURI)&callback=(コールバック関数名) [画像] http://furyu-tei.appspot.com/hb/total/(取得サイトのURI) """ import sys,os,cgi,re import urllib,xmlrpclib import wsgiref.handlers import gae_xmlrpc from xmlrpclib import ServerProxy from google.appengine.ext import webapp from gae_xmlrpc import GAEXMLRPCTransport from google.appengine.api import urlfetch ICON_BASE='http://b.hatena.ne.jp/images/users/normal/%05d.png' XMLRPC_HB='http://b.hatena.ne.jp/xmlrpc' CONTENT_TYPE_JS='application/x-javascript; charset=utf-8' class getTotalHB(webapp.RequestHandler): def get(self): uri=self.request.get('uri') if uri=='': uri=self.request.get('url') if uri: try: proxy=ServerProxy(XMLRPC_HB,GAEXMLRPCTransport()) count=proxy.bookmark.getTotalCount(uri) except: count=0 else: # self.error(404) # return uri='' count=0 icount=count if count<10000 else 9999 icon=ICON_BASE % (icount) json='{uri:\"'+uri+'\",count:'+`count`+',icon:\"'+icon+'\"}'; callback=self.request.get('callback') if callback: json=callback+'('+json+');' self.response.headers['Content-Type']=CONTENT_TYPE_JS self.response.out.write(json) class counterImage(webapp.RequestHandler): def get(self): uri=urllib.unquote(re.compile('^.*?/total/').sub('',self.request.url)) uri=re.compile('%23').sub('#',uri) if uri: try: proxy=ServerProxy(XMLRPC_HB,GAEXMLRPCTransport()) count=proxy.bookmark.getTotalCount(uri) except: # self.error(404) # return count=0 else: count=0 icount=count if count<10000 else 9999 icon=ICON_BASE % (icount) img=urlfetch.fetch(url=icon,method=urlfetch.GET) if img.status_code==200: self.response.headers['Content-Type']=img.headers['Content-Type'] self.response.out.write(img.content) else: self.error(404) def main(): application = webapp.WSGIApplication([ ('/hatena/bookmark/getTotalHB', getTotalHB) , ('/getTotalHB', getTotalHB) , ('/hatena/bookmark/total/.*', counterImage) , ('/hb/total/.*', counterImage) ],debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main()
XML-RPC API使用ではまったので、覚書
Google App Engineではxmlrpclib(ServerProxy)がそのままでは使えない
PythonでXML-RPC APIを使う場合、
import xmlrpclib
from xmlrpclib import ServerProxy
としておいて、xmlrpclibを使えるようにし、
Result=ServerProxy(API_uri).methodName(parameters)
のようにしてコールするのが定石(?)みたいなのですが、Google App Engineでこれをやると、
File "C:\Python25\lib\httplib.py", line 666, in connect for res in socket.getaddrinfo(self.host, self.port, 0, AttributeError: 'module' object has no attribute 'getaddrinfo'
みたいな感じで怒られて、使用できません。
(おそらく)セキュリティ的な制限で、socketを自由に作れなくなっているために発生するエラーだと思われます(通信的な機能は、google.appengine.apiで提供されるurlfetch.fetchを介してのみ使える、ということでしょう、多分)。
Google App Engineでもxmlrpclib(ServerProxy)を使うには
で、きっと解決策があるはず……と思って探してみたら、ありました。
Brizzled: Making XML-RPC calls from a Google App Engine application
We can create our own transport class that uses the google.appengine.api.urlfetch module's fetch() method instead of standard socket access.
xmlrpclibで使用するtrasport classを(socketを使用した)標準のものから、Google App Engineのurlfetch.fetchを使用したものに置き換えることで、使用可能にする、という主旨にとりました。
というわけで、同ページの"The GAEXMLRPCTransport class"のところに書かれているソースをそのまま貼り付けたgae_xmlrpc.pyというファイルを作って、これをGoogle App Engineのアプリケーションディレクトリのトップに置き、
import gae_xmlrpc
from gae_xmlrpc import GAEXMLRPCTransport
という記述を追加してインポートし、XML-RPCコール箇所を
Result=ServerProxy(API_uri,GAEXMLRPCTransport()).methodName(parameters)
のように変更することで、無事動作するようになりました。