前回まで。
- プログラム準備完了。
- ライブラリの不具合修正実施
-
実行開始!
- 一応最初のピークは問題なかった?
- とりあえず集計してみよう
- 外から覗いてみよう
時間ごとの集計をしてみる
ひとまず1週間ほど経過しました。とりあえず今のところ無事に動いているようです。
となると、次にほしいのは現状の入力数とか、時間ごとの集計になるわけですが。
>>> db.xxx.count()
とやれば、カウント自体は上がっているのがわかるのですが、いくらなんでも正確とは言い難い……どうにかして、時間単位のツイート数集計に持ち込む必要があります。
MapReduceで集計
MongoDBでの集計手段は何やら複数あるようですが、とりあえず「薄い本」をもとにMapReduceでもってして集計してみることにします。
……JavaScriptも触るの初めてなんだけどなぁ、とか思いつつ、ググりまくって
db.xxxxx.mapReduce(
// map
function() {
d = new Date(Date.parse(this.created_at) + 32400000);
p = d.getFullYear() + "/" + ("0" + (d.getMonth() + 1)).slice(-2) + "/" + ("0" + d.getDate()).slice(-2) + " " + ("0" + d.getHours()).slice(-2) + ":00:00"
emit(
p,
1
);
},
// reduce
function(key, values) {
return Array.sum(values)
},
{
query: {},
out: "TweetsCount"
}
)
こんな感じに。
(JavaScriptの日時formatって、こんなめんどい手段しかないんですっけ?)
# mongo localhost/TwitterDB check.js
こんな感じで実行すると、別のCollection「TweetsCount」に結果が格納されます。
(ピーク発生個所だけ抜粋)
{ "_id" : "2016/10/28 13:00:00", "value" : 67 }
{ "_id" : "2016/10/28 14:00:00", "value" : 47 }
{ "_id" : "2016/10/28 15:00:00", "value" : 102 }
{ "_id" : "2016/10/28 16:00:00", "value" : 103 }
{ "_id" : "2016/10/28 17:00:00", "value" : 2850 }
{ "_id" : "2016/10/28 18:00:00", "value" : 5317 }
{ "_id" : "2016/10/28 19:00:00", "value" : 4324 }
とりあえず取れてるようです。なーんか全体的に少ないかな?という気もするのですが(ピーク1ケタ足らん気がする……たぶん検索キーの都合)。
この程度の実行なら、さほどの負荷にはならないようです。100日分とかまとまったらまた変わってくるのかもしれませんが……。
DBの中身をチェックしよう
現在動かしている取得プログラムは、画面上の標準出力に何も出ないので、何が格納されたのか確認できません。
MongoDB直接覗けば見れますが、リアルタイムの取得状況までは分からないですし、見るだけなら不要な項目がJSON上にたくさん入っており、率直に言って見づらいです。
この辺どうにかして、リアルタイムで(またはある程度の追従で)ツイート内容がチェックできるスクリプト、がほしいよなぁ、という話になります。
なんかいろいろ手段もありそうなのですが、手っ取り早くて大げさにならなくて、見たいものだけ見るような感じにがりごりやったらこんな感じ(実装2hくらい)。
# !/usr/bin/env python
# -*- coding:utf-8 -*-
from pymongo import MongoClient
import json
import time
# MongoDB 接続関係の変数
HOST = 'mongo' # ホスト
PORT = 27017 # ポート(デフォルト:27017)
DB_NAME = 'TwitterDB' # DB名
COL_NAME= 'xxxxxx' # コレクション名
# ------ ここからメイン処理 ------
try:
Client = MongoClient(HOST, PORT)
db = Client[DB_NAME]
Collection = db[COL_NAME]
print('DB準備完了')
twCnt = Collection.count() # 一番最初のカウント取得
except pymongo.errors.PyMongoError as exc:
# 接続エラー
print('DB接続エラー')
while(True): # 無限ループ
try:
if(Collection.count() > twCnt):
for doc in Collection.find(skip=twCnt):
if("retweeted_status" in doc):
if("extended_tweet" in doc["retweeted_status"]):
# RTの長文
print("RT @" + doc["retweeted_status"]["user"]["screen_name"] + ": " +doc["retweeted_status"]["extended_tweet"]["full_text"])
else:
# RTの短文
print("RT @" + doc["retweeted_status"]["user"]["screen_name"] + ": " +doc["retweeted_status"]["text"])
else:
if("extended_tweet" in doc):
# 長文
print(doc["extended_tweet"]["full_text"])
else:
# 短文
print(doc["text"])
print(u"{name}({screen}) {created} via {src}".format(name=doc["user"]["name"], screen=doc["user"]["screen_name"],
created=doc["created_at"], src=doc["source"]))
print(u"--------------------------------------------------")
twCnt = Collection.count()
# チェック間隔は3秒ごとくらいで(流速次第で10秒でもよいか?)
time.sleep(3)
except KeyboardInterrupt:
# CTRL+C
print('CTRL + C で終了。')
ExitCode = 0
break
(途中のprint()文の記法が変わっているのは、参考資料からのコピペが混じるためです)
やってることは簡単で、定期的にコレクションの数をチェックして、多くなっていれば増えた分を整形して出力する、というものです。
なにやらツイート文言の拡張なる仕様変更があった結果、中盤のifで以下の4パターンに分岐させることに。
・RTのツイートで、かつ拡張仕様(長文)のツイートのパターン
・RTのツイートで、かつ標準仕様(短文)のツイートのパターン
・通常のツイートで、かつ拡張仕様(長文)のツイートのパターン
・通常のツイートで、かつ標準仕様(短文)のツイートのパターン
あとは時間表示してユーザ名表示しておっけー。こんなにウルトラ適当でも、"tail -f"っぽく表示される!ステキ!
小改造
これでもだいたいやりたいことを満たしているのですが、便利を追求したくなるのが人情。
Fav数、RT数を表示
RTされまくってるツイートが、実際どれだけRTされてるの? くらいはほしいよねーとか思うわけで。
print("\n<RT: " + str(doc["retweeted_status"]["retweet_count"]) + " cnt, Fav: " + str(doc["retweeted_status"]["favorite_count"]) + "cnt >")
こんな感じのをRTの際に表示を行う分岐の中に挿入(インデントは揃えること)。RTされるたびにカウントアップしてるのがわかります。
時刻表示を読みやすいように
Twitterから送られるJSON内の時刻表示は、日本人的には読みづらいのでこれを改変。
d = datetime.strptime(doc["created_at"],'%a %b %d %H:%M:%S +0000 %Y') + timedelta(hours=9)
とやってdatetime形式に変換ののち時刻格納個所を
created=d.strftime("%Y/%m/%d %H:%M:%S")
こんなに置き換えれば完成。「from datetime import datetime, timedelta」も忘れずに。
クライアント名表示からHTMLタグを抜く
ツイートがどんなクライアントから送信されたかが表示できるのですが、この項目、HTMLタグが入れられるためか、実際タグが入ってて見づらいことこの上ありません。
「import re」として正規表現を有効にして、doc["source"]を指定している箇所でこんな感じにすればあら不思議、タグがきれいになくなります。
src=re.sub("<.+?>", "", doc["source"])
(タグ以外がなくなる可能性もないわけではないが、今のところうまくいってる。)
つぎになにやるか(ノープラン)
とりあえずこれで、
・ 時間単位でのツイート数
・ リアルタイムで取得しているツイートの表示
ができるようになりました。やはりちゃんとツイートが流れているのを見ると精神的に安心しますね。
次の目標は
・現在一番RTされてるツイートの抽出
・Twitterクライアントの利用統計
・ツイートから店舗(換言)名を抽出して、言及回数を測る
あたりでしょうか。ちまちま作ってみようと思います。
(しつこく続く。)