ビットコインなどについて、人々がどのように思っているのかをツイートから可視化したいと思います。
やりたいこと
- Twitter APIからビットコイン関連ツイートを取得
- ツイートを形態素解析して、ネガポジ判定
- Word Cloudで可視化
- 画像をツイート
ライブラリ
import pandas as pd
from pandas import DataFrame
import json
from janome.tokenizer import Tokenizer
from requests_oauthlib import OAuth1Session
from wordcloud import WordCloud
import emoji
import re
import csv
Twitter APIからビットコイン関連ツイートを取得
Twitter APIの利用方法については、参考サイトなどを参照ください。
関数search_wordのwordに検索したい単語、countに取得するツイート数を指定して(APIのデータ取得回数には制限があるので注意)、該当ツイートを取得します。
下記プログラムでは、jsonファイルに保存したキーを読み込んでいます。
keysfile = 'keys.json'
signature = '#TweetFromPython'
##################### Twitter API #####################
def create_oauth_session(oauth_key_dict):
oauth = OAuth1Session(
oauth_key_dict['consumer_key'],
oauth_key_dict['consumer_secret'],
oauth_key_dict['access_token'],
oauth_key_dict['access_token_secret']
)
return oauth
def search_tweet(search_word, count, oauth):
url = 'https://api.twitter.com/1.1/search/tweets.json'
params = {
'q': search_word,
'count' : count,
'result_type' : 'recent',
'exclude': 'retweets',
'lang' : 'ja'
}
responce = oauth.get(url, params=params)
if responce.status_code != 200:
print("Error code: %d" %(responce.status_code))
return None
tweets = json.loads(responce.text)
return tweets
#### キーを取得
keys = json.load(open(keysfile))
twitter = create_oauth_session(keys)
search_word = 'ビットコイン'
search_count = 200
search = search_tweet(search_word, search_count, twitter) # 検索結果
df_search = DataFrame.from_dict(search['statuses']) # DataFrameに変換
tweets = df_search['text'].tolist() # textカラムがツイート文
ツイートを形態素解析
形態素解析のための前処理として、絵文字やURLを除去します。
emoji.UNICODE_EMOJIはアップデートされて、言語情報も含まれるようになったので、'en'を指定しています。(以前は、emoji.UNICODE_EMOJIだけで大丈夫でした)
URLは正規表現を使って除去しています。
このままだと、ツイッター特有の文字(#、@など)が含まれますが、後のWordCloudでは1文字のアルファベットや記号は無視されるようなので、ここではスルーします。
関数get_wakachiでは、必要な単語だけ分かち書き(単語の間にスペース)に変換します。まず、必要な品詞の単語か識別して、無意味な単語を除外します。さらに、今回は、検索ワード自体も必要ないので除去します。
# 絵文字除去
def remove_emoji(text):
return ''.join(c for c in text if c not in emoji.UNICODE_EMOJI['en'])
# URL除去
def remove_url(text):
return re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-]+', '', text)
# 形態素解析のための前処理
def normalize_tweets(tweets):
normalized = []
for tweet in tweets:
text = tweet
text = remove_emoji(text)
text = remove_url(text)
normalized.append(text)
return normalized
# 必要な単語だけ分かち書きに変換
def get_wakachi(list_text, word, hinshi=['名詞', '形容詞']):
remove_words = ['こと', 'よう', 'そう', 'これ', 'それ', 'もの', 'ここ', 'さん', 'ちゃん',
'ところ', 'とこ', 'の', 'ん', word] # 無意味な単語と検索ワードのリスト
t = Tokenizer()
wakachi = ''
for text in list_text:
malist = t.tokenize(text)
for w in malist:
word = w.surface
part = w.part_of_speech
hit = False
for h in hinshi:
hit = hit or (h in part)
if not hit:
continue
if word not in remove_words:
wakachi += word + ' '
return wakachi
tweets = normalize_tweets(tweets) # 形態素解析のための前処理
wakachi = get_wakachi(tweets, search_word) # 分かち書きに変換
Word Cloudでネガポジを可視化
分かち書きの文字列を使ってWordCloudで可視化しますが、このままだと、単語の意味に関係なく色が付きます。そこで、単語をネガポジ判定して、ネガティブとポジティブで色分けしてみたいと思います。
ネガポジ判定
日本語評価極性辞書をダウンロードして、この辞書から単語のネガポジ判定をします。この辞書には単語とそのネガポジ(ポジティブ=p、ネガティブ=n、どちらでもない=e)が格納されています。
ダウンロード
! curl http://www.cl.ecei.tohoku.ac.jp/resources/sent_lex/pn.csv.m3.120408.trim > pn.csv
WordCloudには、自作の色を返す関数を設定できるので、get_pn_color関数の中でネガポジ判定して色を決めます。単語が辞書に含まれていなければ、e。含まれていれば、その判定結果を返し、判定結果によって色を決めています。色は、ポジティブ=青、ネガティブ=赤、どちらでもない=緑としています。
word_cloudはWordCloudを作成して保存する関数で、wordsに分かち書きを、image_fileに保存するファイル名を、numに表示する頻出単語の最大数を指定しています。
# Initialize Negative-Positive Dictionary
pn_dic = {}
fp = open('pn.csv', 'rt', encoding='utf-8') # ネガポジ辞書を読み込む
reader = csv.reader(fp, delimiter='\t')
for i, row in enumerate(reader): # Pythonの辞書型に変換
name = row[0]
result = row[1]
pn_dic[name] = result
# 色を返す関数
def get_pn_color(word, font_size, **kwargs):
r, g, b = 0, 0, 0
pn = 'e'
if word in pn_dic:
pn = pn_dic[word]
if pn == 'p':
b = 255
elif pn == 'n':
r = 255
else:
g = 255
return (r, g, b)
# Word Cloud
def word_cloud(words, image_file, num,
font_path='C:/Windows/Fonts/MSGOTHIC.TTC'): #日本語フォントを指定
wordcloud = WordCloud(
background_color='white',
color_func=get_pn_color,
max_words=num,
font_path=font_path,
width=800, height=400).generate(words)
wordcloud.to_file(image_file)
return True
image_file = 'word_cloud.jpg'
trend_num = 100
word_cloud(wakachi, image_file, trend_num)
画像をツイート
最後に、保存した画像をツイートします。
def tweet_image(oauth, text, image_file):
url_media = "https://upload.twitter.com/1.1/media/upload.json"
url_text = "https://api.twitter.com/1.1/statuses/update.json"
files = {"media" : open(image_file, 'rb')}
req_media = oauth.post(url_media, files = files)
if req_media.status_code != 200:
print ('media upload failed: %s', req_media.text)
exit()
media_id = json.loads(req_media.text)['media_id']
print ("Media ID: %d" % media_id)
params = {'status': text + '\n' + signature, 'media_ids': [media_id]}
req = oauth.post(url_text, params)
if req.status_code == 200:
print('tweet succeed!')
else:
print('tweet failed')
# ツイート文
tweet_text = 'search word: {}\nsearch tweets num: {}\ntrend words num: {}'.format(search_word, search_count, trend_num)
# ツイート
tweet_image(twitter, tweet_text, image_file)
利益や暴落などの単語が正しく色分けされていることがわかります。これで、ビットコイン関連ツイートのネガポジを可視化することができました。
課題
ネガポジの辞書に含まれない単語が多いため全体的に緑になっていまっています。ガチホなどの特有の単語は別でネガポジ辞書を用意した方がいいです。また、暴騰はネガティブですし、コインがポジティブ、取引がネガティブに分類されているのも紛らわしいかもしれません。
今回の検索ワード「ビットコイン」は、「ビット」と「コイン」に単語が分けられているので、WordCloudに出てきてしまっています。
APIにデータ取得の上限があるので、大量のツイートから可視化するのは難しいかもしれません。
最後に
ビットコイン関連ツイートをネガポジ判定して可視化することで、投資家心理の一部を見ることができました。
ちなみに、「コロナ」でネガポジを可視化すると、このようになりました。
参考サイト
Twitter API
Word Cloud
形態素解析
ネガポジ判定