Twitter API 検索で大量データを収集(CSV)

#開発環境
　Python3.7.3
　Twitter API

#参考にしたページ
https://qiita.com/kngsym2018/items/2524d21455aac111cdee
http://westplain.sakuraweb.com/translate/twitter/Documentation/REST-APIs/Public-API/The-Search-API.cgi
https://syncer.jp/Web/API/Twitter/REST_API/
https://qiita.com/mida12251141/items/97bce06e4167db8ed3d5
https://qiita.com/tomozo6/items/d7fac0f942f3c4c66daf

#事前にやっておくこと
Twitter APIの登録申請。ここでは割愛。
私は申請から許可まで４日ほどかかりました。
「参考にしたページ」の一番目で丁寧な解説がされてます。

#やりたいこと
1レコード1ツイートなテーブル形式の状態でcsvファイルに保存する。
各データには改行はない。
なるべく大量にデータを取得する。

#困ったこと
**・一度に検索できるツイート数の上限は100ツイート**
**・上項の処理の連続実行上限は180回（上限までいくと１５分間の待機が必要）**
**・検索できるツイートデータは一週間前まで**
**・下記５項目はカンマやら改行やらの楽園状態なので文字列処理必須**

・Name
・Screen Name
・Text
・Description
・Location

**※Locationは適当なこと書いてるユーザが多すぎてデータとして使えない**

取得したツイートデータを変数csvlinetextに入れた後の文字列整形を下記のように行った。
他にもいい方法あると思うけど手っ取り早くこれで対処した。

```python:twitterapi.py
            #textの文字列を整形
            csvlinetext = line["text"].strip()            #ツイートデータから改行を削除
            csvlinetext = csvlinetext.replace(",", " ")   #カンマ文字を半角スペースに置換
            csvlines = csvlinetext.splitlines()           #配列変数に格納することで消しきれない改行を削除
            csvlinetext = ''.join(csvlines)               #配列変数の全要素内のデータを結合
```
  
#シナリオ
分かってる人は見なくてもいいやつ。
この流れでコードを組み立てれば上手くいく。

01.Twitter API登録時にもらう認証キーを変数に代入する
02.検索ワードを変数に代入する
03.検索プロパティを配列変数に代入する
04.OAuth認証でセッションを確立する
(ループ処理開始）
05.Twitter API利用制限回数のチェック
06.セッション切断
07.06の制限にかかった場合は１５分待機
08.変数maxidを検索プロパティに指定
09.OAuth認証でセッションを確立する
10.検索実行して結果を変数csvline1に代入する
11.変数の結果をCSVに書き込む
12.ツイートIDを変数maxidに代入する
(ループ処理終了）

#ソース
```python:python
import os
import json
import config
import csv
import time
import math
from requests_oauthlib import OAuth1Session
from pytz import timezone
from dateutil import parser
from dateutil.relativedelta import relativedelta
from datetime import datetime, date, timedelta
import json, time, pytz, re, sys,traceback

#認証キーの変数代入とセッション確立
CONSUMER_KEY = "XXXXXXXXXXXXXXXXXXX"
CONSUMER_SECRE = "XXXXXXXXXXXXXXXXXXX"
ACCESS_TOKEN = "XXXXXXXXXXXXXXXXXXX"
ACCESS_SECRET = "XXXXXXXXXXXXXXXXXXX"

#+****************************************************************
#関数処理
#+****************************************************************

#セッション確立
def getTwitterSession():
    return OAuth1Session(CONSUMER_KEY, CONSUMER_SECRE, ACCESS_TOKEN, ACCESS_SECRET)

# 文字列を日本時間2タイムゾーンを合わせた日付型で返す
def str_to_date_jp(str_date):
    dts = datetime.strptime(str_date,'%a %b %d %H:%M:%S +0000 %Y')
    return pytz.utc.localize(dts).astimezone(pytz.timezone('Asia/Tokyo'))

#API利用制限ステータスチェック
def rate_limit_status():
    limit = 0
    remaining = 0
    reset_minute = 0
    twitter = OAuth1Session(CONSUMER_KEY, CONSUMER_SECRE, ACCESS_TOKEN, ACCESS_SECRET)
    url = "https://api.twitter.com/1.1/application/rate_limit_status.json"
    req = twitter.get(url)
    if req.status_code == 200:
        res = json.loads(req.text)
        limit = res['resources']['search']['/search/tweets']['limit']
        remaining = res['resources']['search']['/search/tweets']['remaining']
        reset = res['resources']['search']['/search/tweets']['reset']
        reset_minute = math.ceil((reset - time.mktime(datetime.now().timetuple())) / 60)
    twitter.close()
    return limit, remaining, reset_minute

#検索条件を配列変数に代入
def getParam():
    params ={
            'count'         : 100,
            'q'             : keyword,
            'result_type'   : 'mixed',
            'max_id'        : maxid
            }
    twitter = getTwitterSession()
    req = twitter.get(url, params = params)
    return twitter, params, req

#+****************************************************************
#メイン処理
#+****************************************************************

#Twitter Endpoint
url = 'https://api.twitter.com/1.1/search/tweets.json'

#ツイートID
maxid = ''
#検索ワード
keyword = '英会話'
#実行日の取得(csvファイル命名用)
today = date.today()

#ツイート取得開始日
start_dt = today
#start_dt = datetime.strptime(start_dt, '%Y%m%d')
dt1 = (start_dt).strftime('%Y-%m-%d')

#ツイート取得最終日
end_dt  = (start_dt - timedelta(days=7))
dt2 = (end_dt).strftime('%Y-%m-%d')

since = str(dt1) + '_00:00:00_JST'
until = str(dt2) + '_23:59:59_JST'

csvline1 = "DateTime,ID,User ID,Name,Screen Name,Text,Follower Count,Friend Count,Favorite Count,Retweet Count,Description,Location\n"
with open("twitter_data_%s_%s.csv" % (keyword,today), "a") as f:
    f.write(csvline1)

for i1 in range(5000):
    #API使用制限回数チェック
    limit, remaining, reset_minute = rate_limit_status()
    print(remaining)
    #API使用制限解除待機
    if remaining == 0:
        print("** Waiting for limit release **")
        time.sleep(60 * (int(reset_minute) + 1))
        time.sleep(1)
    twitter, params, req = getParam()
    if req.status_code == 200:
        res = json.loads(req.text)
        i2 = 0
        i3 = 0
        for line in res['statuses']:
            i3 += 1
            if i3 <= 3:
                continue
            t = str_to_date_jp(line["created_at"])
            maxid = line['id']
            maxid -= 1
            #Nameの文字列を整形
            csvlinename = line["user"]["name"].strip()
            csvlinename = csvlinename.replace(",", " ")
            csvlines = csvlinename.splitlines()
            csvlinename = ''.join(csvlines)
            #Screen Nameの文字列を整形
            csvlinescreen_name = line["user"]["screen_name"].strip()
            csvlinescreen_name = csvlinescreen_name.replace(",", " ")
            csvlines = csvlinescreen_name.splitlines()
            csvlinescreen_name = ''.join(csvlines)
            #textの文字列を整形
            csvlinetext = line["text"].strip()
            csvlinetext = csvlinetext.replace(",", " ")
            csvlines = csvlinetext.splitlines()
            csvlinetext = ''.join(csvlines)
            #descriptionの文字列を整形
            csvlinedescription = line["user"]["description"].replace(",", " ")
            csvlinedescription = csvlinedescription.strip()
            csvlines = csvlinedescription.splitlines()
            csvlinedescription = ''.join(csvlines)
            #locationの文字列を整形
            csvlinelocation = line["user"]["location"].replace(",", " ")
            csvlinelocation = csvlinelocation.strip()
            csvlines = csvlinelocation.splitlines()
            csvlinelocation = ''.join(csvlines)
            csvline1 = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s" % (t,line["id"],line["user"]["id"],csvlinename,csvlinescreen_name,csvlinetext,line["user"]["followers_count"],line["user"]["friends_count"],line["favorite_count"],line["retweet_count"],csvlinedescription,csvlinelocation)
            #末尾に改行を追加
            csvline1 = format(csvline1) + '\n'
            with open("twitter_data_%s_%s.csv" % (keyword,today), "a") as f:
                f.write(csvline1)
            i2 += 1
    else:
        print("Failed: %d" % req.status_code)
f.close()
print(maxid)
print("** Finished **")
```

#収集したデータを分析する
Mecabを使って単語と品詞のみを抽出すると
再頻出ワードの件数やツイートの傾向が分かる。
マーケティング施策の参考情報程度にはなると思いますが、
複数ワードで検索したデータをマージするなどの設計をしないと
有用性の高い分析はできなそう。

```python:twitterapi.py
import csv
import pandas as pd
import MeCab

mecab = MeCab.Tagger ('-d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')

i = 1
f = open('/Users/nomotom/Workspace/py/xxxxxxxx.csv', 'r')
reader = csv.reader(f)
line = f.readline()
for row in reader:
    text = row[5]
    mecab.parse('')
    node = mecab.parseToNode(text)
    while node:
        #単語を取得
        word = node.surface
        #品詞を取得
        pos = node.feature.split(",")[1]
        line = word + ',' + pos + ',\n'
        with open("mecab_data.csv", "a") as f:
            f.write(line)
            print(i)
            i += 1
        node = node.next

f.close()
```