LoginSignup
5

More than 5 years have passed since last update.

posted at

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

前回まで。

  • プログラム準備完了。
  • ライブラリの不具合修正実施
  • 実行開始!
    • 一応最初のピークは問題なかった?
  • とりあえず集計してみよう
  • 外から覗いてみよう

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

 ひとまず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クライアントの利用統計
・ツイートから店舗(換言)名を抽出して、言及回数を測る
 あたりでしょうか。ちまちま作ってみようと思います。

(しつこく続く。)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5