著者:飯尾 淳
前回に引き続き、筆者らの研究グループが開発したTwitterのトレンド分析システムについて解説します。前回は、そもそもTwitterのトレンドとは何かから始まり、開発者登録をしてトレンドAPIを叩き、そのリストを収集する方法まで説明しました。今回は、トレンドをキーにしてツイートを取得する方法と、ツイート群を対象として共起ネットワークグラフを作る方法を紹介します。
シェルスクリプトマガジン Vol.67は以下のリンク先でご購入できます。![]()
![]()
図4 トレンドを収集して分析するプログラムのコード(その1)
#!/usr/bin/env python
###########################################################
# ヘルパーファンクション
###########################################################
import re
# 特殊文字をエスケープする
def escape(s, quoted=u'\'"\\', escape=u'\\'):
return re.sub(u'[%s]' % re.escape(quoted),
lambda mo: escape + mo.group(),
s)
# ノードのプリティプリンタ
def pp_node(ary_of_nodes):
for node in ary_of_nodes:
keyword = escape(node[0])
frequency = node[1]
print("node_hash['{0}'] =
trend.nodes.create(word: '{1}', freq: {2:6.2f})"
.format(keyword, keyword, frequency))
# リンクのプリティプリンタ
def pp_link(ary_of_links):
for link in ary_of_links:
(kw1,kw2) = link[0].split(',')
print("link = Link.create(corr: {0:6.2f})".format(link[1]))
print("node_hash['{0}'].links << link".format(escape(kw1)))
print("node_hash['{0}'].links << link".format(escape(kw2)))
# 値を正規化する。p_fragは百分率とするときTrue
def normalize(hash_obj, p_flag):
maxval = max(hash_obj, key=lambda x: x[1])[1]
factor = 100 if p_flag else 1
return [(x[0], x[1] / maxval * factor) for x in hash_obj]
図5 トレンドを収集して分析するプログラムのコード(その2)
###########################################################
# ワードグラフの作成
###########################################################
import MeCab
# 「PATH_TO_MECAB_NEOLOGD」の部分は環境に合わせて修正
m = MeCab.Tagger("-d PATH_TO_MECAB_NEOLOGD")
stopwords = {'*', 'HTTPS', 'HTTP', 'WWW', 'の', 'ん', 'ン', 'ω', '???'}
def create_graph(keyword, collected, tweets):
ary_of_ary = []
for tweet in tweets:
ary = []
lines = m.parse(tweet).split('\n')
for line in lines:
if line.count('\t') == 0: continue
(kw, prop) = line.split('\t')
props = prop.split(',')
if len({props[-3]} & stopwords) > 0: continue
if props[1] == '固有名詞': ary.append(props[-3])
ary_of_ary.append(ary)
KW_THRESHOLD = 20
kw_dict = {}
counter = 0
for ary in ary_of_ary:
for kw in ary:
if kw in kw_dict: kw_dict[kw] = kw_dict[kw] + 1.0
else: kw_dict[kw] = 1.0
counter = counter + 1
for kw,val in kw_dict.items(): kw_dict[kw] = val / counter * 100
if len(kw_dict) > 0:
kw_dict = sorted(kw_dict.items(),
key = lambda x: x[1],
reverse = True)[0:KW_THRESHOLD-1]
kw_dict = normalize(kw_dict, True)
print("node_hash = {}")
print("trend = Trend.create(label: '{0}', collected:'{1}')"
.format(escape(keyword), collected))
pp_node(kw_dict)
corr_dict = {}
for src in kw_dict:
for dst in kw_dict:
if src == dst: continue
src_w = src[0]
dst_w = dst[0]
sd_pair = src_w + "," + dst_w
if sd_pair in corr_dict: continue
prob = 0.0
for ary in ary_of_ary:
if ((src_w in ary) & (dst_w in ary)): prob = prob + 1.0
prob = 100 * prob / len(ary_of_ary)
if prob > 0: corr_dict[dst_w + "," + src_w] = prob
if len(corr_dict) > 0:
lk_dict = sorted(corr_dict.items(),
key = lambda x: x[1], reverse = True)
# 後半3/4をカットして短くする
lk_dict = lk_dict[0:int(len(lk_dict)*1/4)]
if len(lk_dict) > 0:
lk_dict = normalize(lk_dict, True)
pp_link(lk_dict)
図6 トレンドを収集して分析するプログラムのコード(その3)
###########################################################
# すでに登録済みか否かのチェック
###########################################################
import sqlite3
from contextlib import closing
def has_been_registered(keyword, collected):
# 「PATH_TO_THE_DATABASE_FILE」の部分は環境に合わせて修正
dbfile = "PATH_TO_THE_DATABASE_FILE(development.sqlite3)"
with closing(sqlite3.connect(dbfile)) as conn:
c = conn.cursor()
sql = 'select label, collected from trends \
where label=? and collected=?'
res = (keyword, collected)
c.execute(sql, res)
return (len(c.fetchall()) > 0)
###########################################################
# メイン関数
###########################################################
from twitter import *
from datetime import date
def main():
# WOEID を希望の場所に合わせて指定。「23424856」は日本
woeid = 23424856
# アプリ登録時に取得した情報を下記に設定
CK = 'ADD_HERE_YOUR_CONSUMER_KEY'
CS = 'ADD_HERE_YOUR_CONSUMER_SECRET'
AT = 'ADD_HERE_YOUR_ACCESS_TOKEN'
AS = 'ADD_HERE_YOUR_ACCESS_TOKEN_SECRET'
twitter = Twitter(auth = OAuth(AT,AS,CK,CS))
results = twitter.trends.place(_id = woeid, exclude="hashtags")
d = date.today()
collected = d.strftime('%Y-%m-%d')
for location in results:
for trend in location["trends"]:
keyword = trend["name"]
if has_been_registered(keyword, collected): continue
query_kw = keyword + " exclude:retweets exclude:nativeretweets"
tw_rslts = twitter.search.tweets(q=query_kw,
lang='ja', locale='ja', count=100)
tw_ary = []
for tweet in tw_rslts["statuses"]: tw_ary.append(tweet["text"])
create_graph(keyword, collected, tw_ary)
if __name__ == "__main__": main()
図7 ワードグラフ作成部で出力されるRubyスクリプトの例
node_hash = {}
trend = Trend.create(label: 'ホシガリス', collected:'2020-06-07')
node_hash['アニポケ'] = trend.nodes.create(word: 'アニポケ', freq: 100.00)
node_hash['ポケモン'] = trend.nodes.create(word: 'ポケモン', freq: 56.76)
node_hash['ゴルーグ'] = trend.nodes.create(word: 'ゴルーグ', freq: 29.73)
node_hash['思'] = trend.nodes.create(word: '思', freq: 18.92)
(略)
node_hash['ピカチュウ'] = trend.nodes.create(word: 'ピカチュウ', freq: 5.41)
node_hash['パチリス'] = trend.nodes.create(word: 'パチリス', freq: 5.41)
node_hash['スピアー'] = trend.nodes.create(word: 'スピアー', freq: 5.41)
link = Link.create(corr: 100.00)
node_hash['ポケモン'].links << link
node_hash['アニポケ'].links << link
link = Link.create(corr: 75.00)
node_hash['ゴルーグ'].links << link
node_hash['アニポケ'].links << link
(略)
link = Link.create(corr: 25.00)
node_hash['マユルド'].links << link
node_hash['ドクケイル'].links << link
node_hash = {}
trend = Trend.create(label: '孤独のグルメ', collected:'2020-06-07')
(略)