GAEでTwitterのツイートを圧縮新聞っぽくまとめてみる(2)

GAEでTwitterのツイートを圧縮新聞っぽくまとめてみる(1)という記事を書いたのですが、その続きです。


「圧縮新聞」を作ったを見ると、どうも品詞情報などは全く使っていないようなので、Yahoo! 形態素解析サービスで分割だけして、あとのロジックはそのまま使わせていただきました。


ソースコードはこんな感じ。

# -*- coding: utf-8 -*-

import os
import random
from urllib import urlencode
from google.appengine.ext.webapp import template
from google.appengine.ext import webapp
from google.appengine.api import urlfetch
from django.utils import simplejson
from BeautifulSoup import BeautifulStoneSoup

# ユーザのツイートを圧縮する
class Asshukutter(webapp.RequestHandler):
	def get(self):
		# リクエストを取得
		user = self.request.params['user'] # twitterアカウントが入る
		# 形態素解析したユーザのツイートを取得
		dic = self.segmentMorpheme(user)
		# 圧縮結果を取得
		result = self.asshuku(dic)
		# ちゃんとした結果が得られなかったら得られるまで
		while result == "。".decode('utf_8'):
			result = self.asshuku(dic)
		data = {'result' : result.rstrip("。".decode('utf_8'))}
		path = os.path.join(os.path.dirname(__file__), '../html/index.html')
		self.response.out.write(template.render(path, data))

	# ツイートを圧縮する
	def asshuku(self, dic):
		result = ""
		i = 0
		punctuation = "。".decode('utf_8')
		# 木構造を繋げていく
		preSeg = dic[punctuation][random.randint(0,len(dic[punctuation])-1)]
		result = result + preSeg
		# 「。」が出てくるまで繋げる
		while preSeg != punctuation:
			i = i + 1
			nextSeg = dic[preSeg][random.randint(0,len(dic[preSeg])-1)]
			result = result + nextSeg
			preSeg = nextSeg
			if i == 100:
				preSeg = punctuation
		return result


	# Yahoo! 形態素解析サービスを使って、ユーザのツイートを形態素で分割する
	def segmentMorpheme(self, user):
		dic = {}
		# ユーザのツイートを取得
		tweets = self.searchUserTweet(user)
		# ツイートごとに形態素解析を行う
		for t in tweets:
			urlResult = urlfetch.fetch('http://jlp.yahooapis.jp/MAService/V1/parse',
				method=urlfetch.POST,
				headers={'Content-Type':'application/x-www-form-urlencoded'},
				payload=urlencode({
					'appid': 'アプリケーションID',
					'sentence' : t.encode('utf-8')}))
			# BeautifulStoneSoupを使って結果のxmlをパースする
			soup = BeautifulStoneSoup(urlResult.content.decode('utf_8'))
			preMorpheme = "。".decode('utf_8')
			flag = False
			for morpheme in soup('surface'):
				# 英数字のみからなる文字列を除外
				if not morpheme.renderContents().isalnum() and not flag:
					nowMorpheme = morpheme.renderContents().decode('utf_8')
					# 木構造を作成
					if dic.has_key(preMorpheme):
						dic[preMorpheme].append(nowMorpheme)
					else:
						dic[preMorpheme] = [nowMorpheme]
					preMorpheme = nowMorpheme
				# 英数字直後の形態素は除外
				elif  not morpheme.renderContents().isalnum() and flag:
					flag = False
				else:
					flag = True
		return dic


	# Twitter APIのuser_timelineを使ってユーザのツイートを取得する
	def searchUserTweet(self, user):
		text = []
		preText = ""
		num = 0
		# 入力したユーザ名をクエリとするURL
		url = "http://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + user
		urlresult = urlfetch.fetch(url)
		if urlresult.status_code == 200:
			# 結果をsimplejsonを使ってパース
			jsonload = simplejson.loads(urlresult.content)
			for result in jsonload:
				num = num + 1
				if num % 7 == 0:
					preText = preText + result['text'] + "。".decode('utf_8')
					text.append(preText)
					preText = ""
				else:
					preText = preText + result['text'] + "。".decode('utf_8')
			text.append(preText)
		else:
			text.append("存在しないか非公開なアカウントです。".decode('utf_8'))
		return text

Twitter APIのuser_timelineを使ってユーザのツイートを取得し、それをYahoo! API形態素解析にかけ、それを圧縮新聞と同様にマルコフ連鎖で繋げていきます。
Yahoo!形態素解析は、1リクエストの最大サイズが100KBなので、ツイートを3分割くらいにしてます。


マルコフ連鎖を実現するために、ある形態素をキー、その後ろにくる形態素のリストをバリューをするディクショナリを作成します。
なので、形態素解析にかけ、連続した形態素の、前の形態素をディクショナリのキー、後ろの形態素をディクショナリのバリューのリストに挿入します。


あとはランダムに繋げていきます。
圧縮新聞は4単語を繋げてますが、これは2単語を繋げていきます。


リンクの文字列が結構やっかいで英数字を無視してしまっているんですが、そのためかエラーが出ます。
まぁ出来れば整形して載せます。


一応、作ったものは → 圧縮ったー
while文動きっぱが怖いので上のソースちょっといじって運用してます。


マルコフ連鎖に品詞情報付与したら精度良くなる気がするけど、そうでもないのかな…。