Aug 28, 2008

Google App EngineでAmazon A2S (ECS 4.0) を

ポスト @ 21:37:12 | Google,Python

 Yahoo!検索Webサービスから返ってくるXMLはElementTreeで受けられるようになったのだが、Amazon A2S (ECS 4.0) がどうしても処理できない。minidomも試したのだけど、どうにもならない。何週間も情報を探し求めたのだが、答えに辿り着かない。どうして、こんなことを探さなければならないんだ。他の方法を探そうと心に決めた。もうElementTreeもminidomも使ってやらない。XMLだからってちょっと生意気じゃないか。普通の文字列として処理してやりゃあいいじゃないか。ということに気がついて、正規表現で切り刻んでやった。今頃謝っても許してやらないからそのつもりでいるように(謝られたりはしていないが)。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import cgi import wsgiref.handlers
from google.appengine.ext import webapp from google.appengine.api import urlfetch import urllib import re from re import *
class MainPage(webapp.RequestHandler): def get(self): self.response.out.write(""" <html> <body> <h2>amazon-search</h2> <form action="/result" method="post"> <div><input type="text" name="word" ></div> <div><input type="submit" value="Submit"></div> </form> </body> </html>""")
def amazon(word): re_asin = re.compile('<ASIN>.+?</ASIN>') re_title = re.compile('<Title>.+?</Title>') tags = re.compile('<.+?>') query = {'Service':'AWSECommerceService','AWSAccessKeyId':'xxxxxxxxxxx',\\ 'Operation':'ItemSearch','SearchIndex':'Books','Sort':'-publication_date',\\ 'Keywords':word.encode('utf-8'),'ResponseGroup':'Medium'} url = 'http://webservices.amazon.co.jp/onca/xml?' + urllib.urlencode(query) xml = urlfetch.fetch(url).content items = findall('<Item>.+?</Item>',xml) res = [] for item in items: ASIN = tags.sub('',re_asin.search(item).group(0)) title = tags.sub('',re_title.search(item).group(0)).\\ decode('utf-8') res.append(ASIN+' : '+title) return res
class Result(webapp.RequestHandler): def post(self): word = self.request.get('word') res = amazon(word) results = '<br />'.join(res);
self.response.out.write('<html><body><h2>\\ Amazon Search</h2>Keyword=') self.response.out.write(cgi.escape(word)) self.response.out.write('<br />\n <pre>') self.response.out.write(results) self.response.out.write('</pre></body></html>')
def main(): application = webapp.WSGIApplication([('/', MainPage), ('/result', Result)], debug=True) wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__': main()

 まず、urlfetch.fetch(url).contenで受け取ったXMLを<Item>〜</Item>で分割する。結果はリストに収納されるので、Itemごとにやはり正規表現で<Title>とか<ISBN>とか抽出していけばいい。上では、題名とASINを抜き出している。日本語の文字列はdecode('utf-8')としてやらないと読める文字にならない。試しに「食虫植物」で検索してみると下の画像のようになった。
amz_res.jpg
 なんだ、簡単じゃないか。XMLだからって偉そうに人を馬鹿にしやがって。さて、これで何を作るかはまだ考えていない。

Aug 27, 2008

YahooKWIC

ポスト @ 22:47:27 | Google,Python

 昨日のYahooKWICに英語やドイツ語、ロシア語も選べるようにしてみた。でも、ロシア語などはあまりいい結果が返ってこないような気がする。読めないからよく解らないけど。Yahooはロシア語サイトはあまり集めていないのだろうか。そんなことはないと思ふのだが。英語やドイツ語の結果も驚くほど少ないことがある。英語は英語のYahooサイト、ドイツ語はドイツ語のYahooサイトで検索した方がいいのかな。今度アメリカのサイトで検索して比較してみよう。

 さて、ロシア語の表示なのだが、MacOSXのSafariで見るとこんな感じなのだが、
safari.jpg
ubuntuのEpiphanyだと下のように間延びした表示になってしまうのだ。
epiphany.jpg
いろいろフォント設定を変えたりしても、このままである。ロシアのサイトを見に行けばちゃんと表示されるのだが。ubuntuのMozillaを使えば、まあこんな感じでそれなりにまともに表示される。
mozilla.jpg

 それはさておき、この結果ではどうも満足できない。しっかりした文章の例が出てこない割合が高すぎる。何とかWeb上の文書を綺麗に検索・表示するKWICサイトを作れないものか。

Aug 26, 2008

Yahoo APIでKWIC

ポスト @ 22:18:01 | Google,Python

 ここ三週間ばかり、Google App EngineやらAmazon EC2に悩んでいた。どうもよく解らない。うまく使えない……

 自宅サーバの管理も面倒臭いからこういうものを積極的に利用していこうかと思いながらも、やはり手元に本体があるのが何かと使いやすいような気もするし……何がいいのかと。そんな深刻な悩みじゃありませんが。

 何か使ってみたいと思って試してみたのが、Yahoo!検索Webサービスを利用して検索をして、結果をKWIC(keyword in context)で表示しようというもの。前に作ったことがあるのだけど、あまり出来がよくないので作り直したかったのである。このときはPHPで作ったが、Google App Engineなので今回はPythonだ。とりあえず、こんなふうにしてみた。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cgi
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.api import urlfetch
import urllib
import xml.etree.cElementTree as ET
class MainPage(webapp.RequestHandler):
  def get(self):
    self.response.out.write("""
      <html>
        <body>
        <h2>KWIC-search</h2>
          <form action="/kwic" method="post">
            <div><input type="text"  name="word" ></div>
            <div>
        <select name="lang">
        <option value="ja" selected>日本語</option>
        <option value="ko">韓国語</option>
        <option value="szh">中国語(簡体字)</option>
        <option value="tzh">中国語(繁体字)</option>
        </select>
            <select name="resnum">
        <option value="20" selected>20</option>
        <option value="50">50</option>
        </select>
        </div>
            <div><input type="submit" value="検索"></div>
          </form>
        </body>
      </html>""")
def kwic(word,lang,resnum):
  query = {'appid':'xxxxxx','query':word.encode('utf-8'),\\
    'language':lang,'results':resnum}
  url = 'http://search.yahooapis.jp/WebSearchService/V1/webSearch?'\\
   + urllib.urlencode(query)
  xmlns='{urn:yahoo:jp:srch}'
  xml = urlfetch.fetch(url).content
  dom = ET.fromstring(xml)
  results = []
  for item in dom.findall(xmlns + 'Result'):
    summary = item.findtext(xmlns + 'Summary')
    title = item.findtext(xmlns + 'Title')
    clickurl = item.findtext(xmlns + 'ClickUrl')
    res = {'summary':summary,'title':title,'clickurl':clickurl}
    results.append(res)
  return results
class Result(webapp.RequestHandler):
  def post(self):
    word = self.request.get('word')
    lang = self.request.get('lang')
    resnum = self.request.get('resnum')
    results = kwic(word,lang,resnum)
    table = "<table border='0'>"
    for r_item in results:
      prts = r_item['summary'].split('...')
      for part in prts:
        if part.find(word) > 0:
          lr = part.split(word)
          trow = "<tr><td align='right'>"+lr[0][(len(lr[0])-20):]\\
            +"</td><td><strong>"+word+"</strong></td><td>"+lr[1][0:20]\\
            +"</td><td><a href='"+r_item['clickurl']+"' \\
            target='_blank'>"+r_item['title'][0:10]+"</a></td></tr>"
          table += trow
    table += "</table>"
    self.response.out.write('<html><body><h2>KWIC-yahoo</h2>\n\\
        <p>Keyword = <strong>')
    self.response.out.write(cgi.escape(word))
    self.response.out.write(('</strong></p>\n'))
    self.response.out.write(table)
    self.response.out.write('<hr /></body></html>')
def main():
  application = webapp.WSGIApplication([('/', MainPage),
                                        ('/kwic', Result)],
                                       debug=True)
  wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
  main()

 これの、for item in dom.findall(xmlns + 'Result'):のところが解らなくて、何日も何週間も悩んでいたのだ。こんなこと誰も教えてくれない。何でみんな解るんだ? とにかくこれでやっと結果を処理できるようになったのだ。返ってきた結果にには検索語が複数入っていることがある。たいていは「...」で分割されているので、なるべくそれも反映するようにしてみた。今日は余裕がないので、四言語しか選べないようになっているけれども、これは後で追加したい。

 これはhttp://yahookwic.appspot.com/で使えるようにしておいた。たくさん結果を集めて前後の単語の頻度順なんかで並べ替えられるようにすると格好いいのだが、今はそんなことをする元気はない。日本語だと形態素解析とかしなければならないし。がんばってやったことはあるんだけど。

Aug 02, 2008

Google App Engineを使う(3)

ポスト @ 21:55:01 | Python,Google

 アプリケーションのアップロードは、ZDNet Japanの記事を参考にした。ほとんどそこに書いてある通りなのだが、
create.jpg
ここのところで、Application Identifierにwordcountを使おうとしたら、すでに使っている人がいるのか、駄目だと云われてしまい、wordcount0801という名前にした。そしてSaveをクリックすると、こんな画面が! (ここではなかったかも。実はよく覚えていないが、この一連の作業のどこかに出てきたのだ)
sms_confirm.jpg
何だこれは? 携帯電話なんか私は持っていないのだが。と思ったが、Wilcomが使えるので大丈夫だった。Wilcomのユーザ名(メールアドレスの@の前)を入れて送信すると、暗証番号が携帯メールに送られてくる。それを入力して初めて、次に進めるのだ。そんな話は聞いていないのでちょっと慌てた。無事に、
registered.jpg
という画面が出た。

 そこで、先ほどの作業ディレクトリに戻って、

appcfg.py update wordcount0801/
というコマンドを実行すると、アップロードできるという訳である。アクセスしてみると、動いているようじゃないか! 多少なりともPythonが使えてよかった。RubyとかPHPは使えないのだ。

 ただでこんなことができるなんてGoogleは太っ腹ではないかと喜び、本当の目的にアプリケーションを作ってアップロードした。その内容はここには書かない。秘密である。今度は携帯メールに合い言葉が送られてきたりはしない。その代わり、「これをアップロードしたらあと8個アプリケーションを作れることになりますよ」という表示が出る。どうやら全部で10個しか作れないらしい。無料だし、まだpreview releaseだし。私には10個も作れれば十分だと思い、アップロードして動かしてみたら、DeadlineExceededErrorというエラーが出てしまった。タイムアウト時間は約8秒らしい。ローカル環境でできないこと、あるいは時間がかかりすぎることをやってもらおうと思っていたのに。残念。他の動作環境を探さなければ。そこで考えたのが、Amazon EC2である。その話は明日以降。

Google App Engineを使う(2)

ポスト @ 21:18:46 | Python,Google

 実際にアップロードするアプリケーションがHello World!ではあまりにも情けないので、今年二月に書いたPython練習帖:単語を数えるを使ってみることにした。wordcount0801というディレクトリを用意し、そこで、

import cgi
import re
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
class MainPage(webapp.RequestHandler):
  def get(self):
    self.response.out.write("""
      <html>
        <body>
          <form action="/result" method="post">
            <div><textarea name="content" rows="3" cols="60"></textarea></div>
            <div><input type="submit" value="Count!"></div>
          </form>
        </body>
      </html>""")

class Wordcount(webapp.RequestHandler):
  def post(self):
    self.response.out.write('<html><body>Result:<pre>')
    content = cgi.escape(self.request.get('content'))
    if len(content) < 10000:
      noLine = re.sub('(\n)+', ' ', content)
      sLine = re.sub('[.!:?]', ' ', noLine)
      sLine2 = re.sub("[,\-']", ' ', sLine)
      sLine3 = re.sub("[,';:]", '', sLine2)
      sLine3 = re.sub('"', '', sLine3)
      words = sLine3.split(' ')
      words = [w for w in words if len(w) > 0]
      wDict = {} 
      for w in words:
        if wDict.has_key(w):
          wDict[w] += 1
        else:
          wDict[w] = 1
      keys = wDict.keys()
      keys.sort()
      optxt = ""
      for key in keys:
        optxt += key + ":" + str(wDict[key]) + "\n"
      optxt += '\n There are a total of ' + str(len(wDict)) + ' words in this text.'
    else:
      optxt = 'Your text is too long for this application to count words'
    self.response.out.write(optxt)
    self.response.out.write('</pre></body></html>')
application = webapp.WSGIApplication(
                                     [('/', MainPage),
                                      ('/result', Wordcount)],
                                     debug=True)
def main():
  run_wsgi_app(application)
if __name__ == "__main__":
  main()
というPythonのスクリプトを書いて、これをwordcount.pyとして保存する。次に、app.yamlというファイルを用意する。
application: wordcount0801
version: 1
runtime: python
api_version: 1
handlers:
- url: /.*
  script: wordcount.py
これがローカル環境で動いたので、これが無事にアップロードできれば、http://wordcount0801.appspot.comで利用できるようになるはずだ。それはまた、次項で。

Google App Engineを使う

ポスト @ 20:56:44 | Python, Google

 Google App Engineというのが話題になっていたらしいのだが、世間の流行にあまり関心のない私はよく知らなかった。が、突然「便利なのかも知れない!」と思い、使ってみることにした。
 申請が面倒くさいかと思ったら、Googleアカウントを持っていれば簡単らしい。まずは、ローカル環境でプログラムを書くための設定をすることとした。

 ダウンロードページでLinux版Google App Engine SDKをダウンロード。zipファイルなので展開して適当なところに置く。とりあえず、ホームディレクトリに置いた。dev_appserver.pyとappcfg.pyのシンボリック・リンクをパスの通っているディレクトリに張った。
 動作確認はhello worldと決まっているらしいので、Hello, World!に書いてあるように、helloworld.pyとapp.yamlの2ファイルを作ってhellowというディレクトリに置いた。ディレクトリはどこに作ってもいいのだと思う。とりあえず、さっき展開したフォルダの中に作った。hellowディレクトリの一つ上の階層で

dev_appserver.py hello/
と打つと、
INFO     2008-08-01 02:12:09,432 appcfg.py] Server: appengine.google.com
Allow dev_appserver to check for updates on startup? (Y/n): Y
dev_appserver will check for updates on startup.  To change this setting, edit /home/tolle_et_lege/.appcfg_nag
INFO     2008-08-01 02:12:14,915 appcfg.py] Checking for updates to the SDK.
INFO     2008-08-01 02:12:15,532 appcfg.py] The SDK is up to date.
WARNING  2008-08-01 02:12:15,532 datastore_file_stub.py] Could not read datastore data from /tmp/dev_appserver.datastore
WARNING  2008-08-01 02:12:15,532 datastore_file_stub.py] Could not read datastore data from /tmp/dev_appserver.datastore.history
INFO     2008-08-01 02:12:15,698 dev_appserver_main.py] Running application helloworld on port 8080: http://localhost:8080
INFO     2008-08-01 02:22:16,804 dev_appserver.py] "GET / HTTP/1.1" 200 -
INFO     2008-08-01 02:22:16,805 dev_appserver_index.py] Updating /home/tolle_et_lege/googleApp/hello/index.yaml
というような状態になる。そこで、ブラウザでhttp://localhost:8080を開くと、下のようなhellow worldという表示が出る。

hellow.jpg

 プログラムのアップロードなどは次項で。

Jun 26, 2008

ATOKで歴史的仮名遣ひ

ポスト @ 23:19:06 | ubuntu,MacOSX

 ubuntuにインストールしたATOK X3 for Linux をいろいろ試してゐたら、自分で作った歴史的仮名遣ひ辞書を標準辞書に追加したり、ユーザ辞書として追加したりしゐたのだけれども、この頃のATOKの場合、「文語モード」にすればそれだけでもう問題なく歴史的仮名遣ひが変換できるやうだ。前から「文語モード」のことは知ってゐたのだが、「文語モード」っていふくらゐだから、文語で書かなければならないと思ひこんでゐたのだ。普通の現代文(口語文)を入力してもちゃんと変換してくれるではないか。標準モードだと、自分でハ行四段活用の動詞を追加して、たとへば「云ふ」といふ語を登録して安心してゐても、「いはう」と入力しても「云はう」と変換されないのである。未然形(已然形)は標準モードでは変換してくれないのである。それが、文語モードでは「いはう」と入力すれば「云はう」と変換してくれる。私が大事に育ててきた歴史的仮名遣ひ変換用辞書は不要だったやうだ。この際、MacもATOKにしてしまはうかな。ATOK 2008 for Mac といふのが来月出るといふではないか。買ってしまはうか。さうして、文語モードで使ってみようか。

 ああ、気がついたら歴史的仮名遣ひで書いてゐるぢゃないか。ここでは現代仮名遣ひで書くことにしてゐたのに。まあ、いいか、今日は。

以前のログ