読者です 読者をやめる 読者になる 読者になる

COBOL技術者の憂鬱

COBOLプログラマは不在にしています

映画に関するつぶやきを取得する

今週のお題は、これですね。

データベースから映画タイトルを取得し、TwitterSearchAPIを使って、その映画に関するつぶやきを取得するプログラムを作成し、CRONで回す。


前回のエントリでは、某サイトから映画のタイトルを定期的に取得してくるプログラムを書いたので、今回はその内容を受け、twitterから映画に関するつぶやきを取得するプログラムを書いてみます。
出来上がったのは、以下のプログラムです。今回も短くまとまったのですが、途中でかなり泥臭いことをやるはめになってしまいました。




【get_tweet.py】

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from google.appengine.api import urlfetch
import re
import datetime

import movies
import htmlentity2unicode

query = movies.Movies.gql('ORDER BY update_date DESC,check_time ASC')
movie = query.get()

if movie:
  api_url = 'http://search.twitter.com/search.atom?q=' + movie.title.encode('utf-8') + '&locale=ja&since_id=' + movie.since_id.encode('utf-8')
  result = urlfetch.fetch(api_url)
  if result.status_code == 200:
    pattern = re.compile(r'<id>tag:search.twitter.com,2005:(.*?)</id>')
    tweet_ids = pattern.findall(result.content)
    if len(tweet_ids) > 1:
      movie.since_id = tweet_ids[1]
      pattern2 = re.compile(r'<title>(.*?)</title>')
      tweet_titles = pattern2.findall(result.content)
      movie.tweet_count = movie.tweet_count + len(tweet_titles) - 1
      for i in range(len(tweet_titles)):
        if i > 0:
          tweet = htmlentity2unicode.htmlentity2unicode(tweet_titles[i]).encode('utf-8')
    movie.check_time = datetime.datetime.today()
    movie.put()


前回作成したプログラムによって、映画タイトルがデータストアに20件保存されているわけですが、まず処理の先頭で、そこから1件だけ取得しています。
この時「更新日で降順、チェック時間で昇順」と指定して1件取得しているのがミソですね。こうしておくことで、このスクリプトが実行される度に、データストア内の最新映画タイトル20件の中から、1件ずつ順番に処理されていくことになります。データの持ち方をほんの少し工夫するだけで、コードが縮まることのよい例になっていると思います。


そして、その後ろの処理では、取得した映画タイトルを使ってTwitterSearchAPIを叩きにいきます。ここで取得したつぶやきは数値文字参照になっているので、これを普通の文字列に変換するのに、こちらのプログラムを利用させてもらっています。


最後に、最新のつぶやきIDとチェック時間をデータストアに保存して終わりです。これらの情報は、次回に同じ映画タイトルでTwitterSearchAPIを叩きにいく際に、必要になってきます。そうそう、今回取得したつぶやき数もカウントしてデータストアに保存しておきましょう。これは、次回のエントリで映画に関するスコアを算出する際に必要になってきそうです。




前回作成したプログラムに対しても、今回、多少修正を加えていますので再掲載しておきます。
データのもたせ方をちょっと変えたことによる対応ですね。




【get_movie_title.py】

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from google.appengine.ext import db
from google.appengine.api import urlfetch
import re
import datetime

import movies

result = urlfetch.fetch('http://movie.nifty.com/cs/movie/list/1.htm')
if result.status_code == 200:
  pattern = re.compile(r'<span class="item"><a href="(.*?)">(.*?)</a>')
  movie_titles = pattern.findall(result.content)
  for movie_title in movie_titles:
    movie_title_u = unicode(movie_title[1],'Shift_JIS')
    query = movies.Movies.gql('WHERE title = :title',title=movie_title_u)
    movie = query.get()
    if movie:
      movie.update_date = datetime.date.today()
    else:
      movie = movies.Movies()
      movie.title = movie_title_u
      movie.update_date = datetime.date.today()
      movie.check_time = datetime.datetime.today()
      movie.tweet_count = 0
      movie.score = 0
      movie.since_id = '0'
    movie.put()


データストアに持たせる項目の定義については、外部ファイルにくくりだしておくことにします。
二つのプログラムに個別に定義しておくのはよくありませんからね。




【movies.py】

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from google.appengine.ext import db

class Movies(db.Model):
  title = db.StringProperty()
  create_date = db.DateProperty(auto_now_add=True)
  update_date = db.DateProperty()
  check_time = db.DateTimeProperty()
  tweet_count = db.IntegerProperty()
  score = db.IntegerProperty()
  since_id = db.StringProperty()


そして、app.yamlとcron.yamlを以下のように更新しました。
/get_tweetというurlと、今回作成したget_tweet.pyをひもづけて、それが6分間隔で実行されるようにしてあります。
映画タイトルは20件登録されているので、こうしておけば2時間で1周することになりますね。




【app.yaml

application: movie-twitter
version: 1
runtime: python
api_version: 1

handlers:
- url: /get_movie_title
  script: get_movie_title.py
  login: admin
- url: /get_tweet
  script: get_tweet.py
  login: admin


【cron.yaml

cron:
- description: get_movie_title
  url: /get_movie_title
  schedule: every 24 hours
- description: get_tweet
  url: /get_tweet
  schedule: every 6 minutes

そして最後に、「movie-twitter」というアプリケーションIDを取得し、本番環境にデプロイしてみました。
あれ、もうバックエンド部分が本番環境で稼働してしまいましたね。これでうまく動作しているかどうか、しばらく観察してみようと思います。



次回のエントリでは、今回取得したつぶやきから、映画に関する評判をスコアとして算出する部分と、それをどうユーザーにみせていくかというところを考えていきたいと思います。
あと一息ですね。