PythonでTwitter APIを使いたい！

##環境
ubuntu 16.04 LTS
python 3.7.3

##やること
Twitterが公開しているAPIをpythonで使います。APIの種類は色々ありますが、定番のsearch/tweetsを使ってツイートを取得します。

##Twitter APIについて
年々制限が厳しくなっているTwitter API。特にUser Streams APIが廃止されるなど、サードパーティのクライアントの開発はなかなか厳しい状況です。一方でデータの収集・分析ということであれば工夫次第で色々できますので、どんどん使っていきましょう。

###公式ドキュメントの日本語訳
http://westplain.sakuraweb.com/translate/twitter/Documentation/REST-APIs/Public-API/REST-APIs.cgi
基本的に全部書いてあります。どんなAPIがあるのか眺めるだけでも面白いです。

###制限
APIの多くはリクエスト回数などに制限があります。search/tweetsの場合は1回のリクエストで100ツイートまで/15分ごとに180回までリクエストが可能なので、一度に取得できるツイート数は最大18000となります。公式ドキュメントにも記載がありますが、https://qiita.com/mpyw/items/32d44a063389236c0a65 で一覧が見れます。

また、search/tweetsに関して、検索できるのは1週間前のツイートまでです。これが結構キツい...。リクエスト回数の制限はツイートIDやツイート日時の情報を利用して工夫できるのですが、こっちはどうしようもありません。比較的長い期間のデータを収集したい場合はこれからツイートされるものを集めていくしかないです。
（過去にはツイート日時の範囲を指定することで1週間より前のツイートも取得できたようなのですが、今は多分無理です。もし成功している記事などがあればぜひ教えてください！）

###データ構造
search/tweetsだけ見ても、1つのツイートに対してかなりのデータが取得できるのでざっくり説明します。まず、データはjson形式で返されます。最初にstatusesとsearch_metadataの2つがあり、statusesの方に各ツイートに関するデータが格納されています。search_metadataの方には検索ワードや取得したツイート数、一番新しい/古いツイートのIDなどがあります。ツイート内容はstatuses直下のtextにあります。詳細や他のAPIについては公式ドキュメントを参照てください。

###APIを利用するために必要なもの
僕が初めて使った時は既に開発者の登録が必要でした。他にわかりやすく説明しているサイトがたくさんあるので具体的な手順は省略しますが、最終的にコンシューマーキー、アクセストークン、それぞれのシークレットキーの、４つの認証キーを取得してください。

##補足
Twitter APIではOAuthによりアクセス権限が認証されます。現在の標準はOAuth2.0ですが、このAPIは1.0での認証になります。またAPIやスクレイピング全般に言えることですが、リクエスト・レスポンスのやり取りはbytes型⇔str型の変換が必要になります。

この辺の話を含めて、スクレイピングなどWebデータの収集・分析に関する書籍はこれがオススメです。
https://www.amazon.co.jp/dp/4774183679

##pythonでAPIを使う
search/tweetsでデータを取得し、テキスト（ツイート内容）だけを抽出します。OAuth認証にはrequests_oauthlibを利用します。

```
$ pip install requests_oauthlib
```
###コード

```python
import urllib
from requests_oauthlib import OAuth1
import requests
import sys


def main():

    # APIの秘密鍵
    CK = 'xxxxxxxxxxxxxxxx' # コンシューマーキー
    CKS = 'xxxxxxxxxxxxxxxx' # コンシューマーシークレット
    AT = 'xxxxxxxxxxxxxxxx' # アクセストークン
    ATS = 'xxxxxxxxxxxxxxxx' # アクセストークンシークレット

    # 検索時のパラメーター
    word = '別れました ありがとう' # 検索ワード
    count = 100 # 一回あたりの検索数(最大100/デフォルトは15)
    range = 5 # 検索回数の上限値(最大180/15分でリセット)
   # ツイート検索・テキストの抽出
    tweets = search_tweets(CK, CKS, AT, ATS, word, count, range)
    # 検索結果を表示
    print(tweets[0:4])


def search_tweets(self, CK, CKS, AT, ATS, word, count, range):
    # 文字列設定
    word += ' exclude:retweets' # RTは除く
    word = urllib.parse.quote_plus(word)
    # リクエスト
    url = "https://api.twitter.com/1.1/search/tweets.json?lang=ja&q="+word+"&count="+str(count)
    auth = OAuth1(CK, CKS, AT, ATS)
    response = requests.get(url, auth=auth)
    data = response.json()['statuses']
    # 2回目以降のリクエスト
    cnt = 0
    tweets = []
    while True:
        if len(data) == 0:
            break
        cnt += 1
        if cnt > range:
            break
        for tweet in data:
            tweets.append(tweet['text'])
            maxid = int(tweet["id"]) - 1
        url = "https://api.twitter.com/1.1/search/tweets.json?lang=ja&q="+word+"&count="+str(count)+"&max_id="+str(maxid)
        response = requests.get(url, auth=auth)
        try:
            data = response.json()['statuses']
        except KeyError: # リクエスト回数が上限に達した場合のデータのエラー処理
            print('上限まで検索しました')
            break
    return tweets


if __name__ == '__main__':
    main()
```
###実行結果
見やすいように改行してます。ユーザーIDとURLはさすがに伏せました。

```
['別れました。ありがとうございます。楽しかったです😌😌', 
'別れました。\n今までありがとう',
 '@xxxxxxxxxxx なるほど、アドバイスありがとうございます。自分はもう別れましたが周りから、あの子はメンヘラ？っぽいといわれるほどの子と付き合っていました。自分がメンヘラ化しないよう、気を付けていきたいと思います…', 
'けいくんと別れました。\nすごく短い間だったけど、けいくんからもらったものはすごくすごく多くて…私なんかよりいい人なんていっぱいいるよ。お互い頑張ろうね！この共同アカウントは来週には閉じます🥺みんな今まで応援ありがとうございました❤️ https://t.co/xxxxxxxxxxx']
```
##スクレイピングについて
一般的なスクレイピングの方法を使えば、リクエスト回数や期間の制限なくツイートを取得できます。しかし、APIが公開されている以上そちらを利用するべきであり、スクレイピングが横行すると運営も対策を講じる必要に。行儀良くAPIを使いましょう。