Twitter Streaming APIで取得した内容をJSON形式で書き出す


Streamingでツイート取得して、JSONファイルの書き出しまでやってみたのでメモ。
調べるとtweepyを使ったツイート取得が多かったので、もしかしたらそっちが主流なのか?
だからあえて使わないぜ!みたいな姿勢ではなく、REST APIで作ったものそのまま流用できないかってことでtweepyは使わず。
ちなみにpython3.5

このあたりを参考
https://dev.twitter.com/streaming/reference/post/statuses/filter
http://qiita.com/kenmatsu4/items/23768cbe32fe381d54a2
http://qiita.com/yubais/items/dd143fe608ccad8e9f85
http://qiita.com/Gen6/items/ce83a77217a86a26d907

ソース

# -*- coding:utf-8 -*-

from requests_oauthlib import OAuth1Session
import json, datetime, time, sys

KEYS = { # 自分のアカウントで入手したキーを下記に記載
    'consumer_key':'*******',
    'consumer_secret':'*******',
    'access_token':'*******',
    'access_secret':'*******'
   }

# Twitter APIへの接続
twitter = OAuth1Session(KEYS['consumer_key'],KEYS['consumer_secret'],
                        KEYS['access_token'],KEYS['access_secret'])

nRstTm = 15 # Rate Limited待機時間
stKeyWrd = 'twitter' # 検索ワード

url = 'https://stream.twitter.com/1.1/statuses/filter.json'

# ツイートのdatetimeを日本標準時間に変換
def str_to_datetime_jp(datetimes):
    dts = datetime.datetime.strptime(datetimes,'%a %b %d %H:%M:%S +0000 %Y')
    return(dts+ datetime.timedelta(hours=9)).strftime("%Y-%m-%d %H:%M:%S")

f = open("teststream.json", "w",encoding='utf-8')
while(True): 
    try:
        req = twitter.post(url,
                    data= {"track":stKeyWrd},
                    stream = True)

        if req.status_code == 200: 
            for line in req.iter_lines():
                work = json.loads(line.decode('utf-8'))
                workuser = work['user']
                tweet_json = {"tweetId":workuser["id"],
                              "name":workuser["name"],
                              "screen_name":workuser["screen_name"],
                              "text":work["text"],
                              "created_at":str_to_datetime_jp(work["created_at"])
                              } 
                print('write')
                json.dump(tweet_json, f, ensure_ascii=False, indent=2,sort_keys=True)

        # 短時間でアクセスしすぎたら420エラーになるので待機
        elif req.status_code == 420:
            print('Rate Limited:',nRstTm,'分待機')
            time.sleep(nRstTm)
        else:
            # とりあえず処理抜けとく
            print('req.status_code',req.status_code)
            break

    # streamで取得できない場合、以下例外になるので再度取得しなおし
    except json.JSONDecodeError as e:
        print('再取得')
        pass
    except KeyboardInterrupt:
        print('処理終了')
        f.close()
        break
    except:
        print('except', sys.exc_info())
        pass

処理で詰まったところ

ここでstreaming開始して、statusが200ならループしてJSONを書き込むというシンプルな作り。
俺のレベルが低すぎてこれが正直精いっぱいなだけ。

詰まったのがreq.iter_lines()で1件ずつ処理しますよってところ。

            for line in req.iter_lines():
                work = json.loads(line.decode('utf-8'))

ここのデータの渡し方で、変数lineがbytes型なので、単純にjson.loadsするとstr型にしろって怒られちゃう。
decode('utf-8')で変換してあげないとダメってことを知らず。

あと全然ツイートされていないものでstreamingするとjson.loadsしたくても変数lineにないで!ってなるので以下でcatchしてあげた。

    except json.JSONDecodeError as e:
        print('再取得')
        pass

分からないところ

実装していて分からないところが2つあって上手くカバーできてない。
1. キーワード検索で助詞を含むワードが取れない
2. 420エラーがでまくる

キーワード検索で助詞を含むワードが取れない

どうやらキーワード検索したワードの前後に何かしらの言葉がくっついていると取得できないみたい。
https://github.com/tsupo/Twitter-API-Specification--written-in-Japanese-/blob/master/twitterAPI.txt

例えば「アイカツ!」をキーワード検索して、「#アイカツ!」みたいにハッシュタグ(ワード間に半角スペース)であればいいんだけど、「アイカツ!で一番かわいいのは霧矢あおいちゃん」みたいに助詞が入ると途端にとってこない。

アイカツスターズ!のtwitterの実況取りにくいから正直すげー困る。

なんかこれ回避方法とかあるのか。自信ニキご教授オナシャス!

420エラーがでまくる

短期間で何度も接続・再接続しようとするとお前つなぎ過ぎじゃって言われて420エラーになってしまう。
https://dev.twitter.com/overview/api/response-codes
https://dev.twitter.com/rest/public/rate-limiting

420エラーで再接続待ちするのはいいけど、実際何分待てばいいのかさっぱりわからん。RESTだときっちり決まってるのに、なぜstreamingは明言されてないんだよ。

正直このエラーになって再接続待ちしている間にツイート流れるので、このエラーが出力しないように回避したいんだけど、やり方不明。そもそもできんの?

実装が悪さしている可能性が結構高い気はしてる。RESTと同じように何度もアタックしちゃうのは悪手な気がするし。