Posted at

ツイートを長期間収集する実験(集計&中身確認編)

More than 3 years have passed since last update.


前回まで。



  • プログラム準備完了。


  • ライブラリの不具合修正実施


  • 実行開始!



    • 一応最初のピークは問題なかった?




  • とりあえず集計してみよう


  • 外から覗いてみよう


時間ごとの集計をしてみる

 ひとまず1週間ほど経過しました。とりあえず今のところ無事に動いているようです。

 となると、次にほしいのは現状の入力数とか、時間ごとの集計になるわけですが。

>>> db.xxx.count()

 とやれば、カウント自体は上がっているのがわかるのですが、いくらなんでも正確とは言い難い……どうにかして、時間単位のツイート数集計に持ち込む必要があります。


MapReduceで集計

 MongoDBでの集計手段は何やら複数あるようですが、とりあえず「薄い本」をもとにMapReduceでもってして集計してみることにします。

 ……JavaScriptも触るの初めてなんだけどなぁ、とか思いつつ、ググりまくって


check.js

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くらい)。


InsertChecker.py

#!/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クライアントの利用統計

・ツイートから店舗(換言)名を抽出して、言及回数を測る

 あたりでしょうか。ちまちま作ってみようと思います。

(しつこく続く。)