PythonでSearch Tweets: Full Archive / Sandboxを使う

[前の記事](https://qiita.com/johnsakagami/items/7202368fe11e570e060e)で卒論のためにPythonを始め、Twitter APIでいろいろやろうとしました。
Python歴ももう10日ではありません。まあそれほどスキルは上がっていませんが……。
##今回の前提：Twitter公式ウェブサイト上での検索は全件表示されているのか？
結局卒論のデータ集めは、過去のツイートまで全アクセス権を持つTwitter公式ウェブサイト上での検索となりました。原始的～。
さて、そうやってデータの数を取っていっていたのですが、過去にさかのぼるほど当該キーワードでの検索結果の件数が**予想以上に**減っていきます。

そこで頭をよぎる嫌な予感。

　**「もしかして、Twitter公式ウェブサイト上の検索って全件表示されていないの……？」**

確かに、「過去のツイートになればなるほど、閲覧ニーズが下がるため、検索結果が間引かれる」といったことも考えられなくはないです。
これでは定量的分析もクソもありません。卒論の危機です。(；・∀・)

##Twitter公式ウェブサイト上での検索の仕様
Twitter公式ウェブサイト上での検索（以下、Twitter検索）には、いくつかの検索結果の表示形式があります。
![1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508374/fbe9b464-1252-eb36-25c4-258ab5723094.png)
よく焦点となるのは、「話題のツイート」と「最新」の違いです。
（以前に、「話題のツイート」は「話題」、「最新」は「すべてのツイート」という名称だった時期がありました。）

Twitter公式のアナウンス（「[検索結果についてのよくある質問](https://help.twitter.com/ja/using-twitter/top-search-results-faqs)」）によると、
>**私の好きなツイートが話題のツイートに表示されないのはどうしてですか？**
話題のツイートは、検索内容との関連性が最も高いものです。Twitterでは、ツイートの人気（多くの人がリツイートや返信などを通じて反応したり共有したりしているなど）、含まれているキーワードをはじめ、数多くの要素に基づいて関連性を判断しています。自分の好きなツイートが話題のツイートに表示されない場合、そのツイートは検索内容との関連性があまり高くないということです。検索キーワードにマッチする最近のツイートを表示するには、[すべてのツイート]（[最新]）をクリックまたはタップしてください。

もちろん、今回のデータ取得も「最新」のタブを使っていました。
以前まで「すべてのツイート」という名前だったのだから、全件表示されとるやろ……というのも思ったのですが、現時点では「最新」という名前であり、かつ公式アナウンスに「全件表示されています」という明確なアナウンスもなかったので、確証が取れない状況です。

##今回やったこと
そこで、Twitter APIの全件検索の無償試用版（Search Tweets: Full Archive / Sandbox）使って、Twitter検索の結果は全件表示されているのかを検証することにしました。
リクエスト数の制限（Sandboxは50リクエスト/月）により、量的な検索はできませんが、検証のために数回検索出来ればいいや、というノリです。
あと、Search Tweets: Full Archive / Sandboxに関するまとまった日本語の記事が管見で見当たらなかったので、（きわめて僭越ながら）いったんまとめてみようと思います。
Twitter developerアカウントの取得などはすでに終わっている状況です。
###①Dashboardでの開発環境設定
まず、developerアカウントでログインし、DashboardからSearch Tweets: Full Archive / Sandboxの開発環境を設定しないといけないっぽいです。
![２.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508374/41156a2e-85b3-1f16-92c5-9479bba1e6ff.png)
上図はすでに設定が終わっているので何も表示されていませんが、最初の状態では「You must first set up a dev environment before accessing an endpoint and viewing usage.」というのが下の方に表示されています。「set up a dev environment」をクリックし、「Dev environment label」と、どのAppでSearch Tweets: Full Archive / Sandboxを使うのかを設定します。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508374/1d0ed6e4-c358-2a11-fb62-205168a2ac9e.png)
Dev environment label（今回では`development`）は後で使いますし、App Nameは認証に使うコンシューマキー云々のものとそろえる必要があります。
###②コード中で躓いた点
[以前の無償版の検索で使ったコード](https://qiita.com/johnsakagami/items/7202368fe11e570e060e)がそのまま使えるかと思っていたのですが、まずエンドポイントのURLを書き換える必要がありました（それはそう）。
無償版でうまく行っていたのは
　`https://api.twitter.com/1.1/search/tweets.json`
だったのですが、今回は
　`https://api.twitter.com/1.1/tweets/search/fullarchive/development.json`
となりました。（もちろん[Search Tweets: Premium search APIsレファレンス](https://developer.twitter.com/en/docs/tweets/search/api-reference/premium-search)に記述があります。）
上に触れていますが、設定したDev environment labelがこのURLにも含まれます。この場合では`development`の部分です。適宜自分のに合わせて書き換えてください。

また、無学なのでなぜかわからないのですが、Standard search APIでの検索と、Premium search APIsでの検索で、使うパラメータの名前が違います。なので、無償版で使っていたコードから、その点を書き換えないと動きませんでした。
![5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508374/67af28c6-cb25-7b12-aa72-363c803f5f2c.png)
（↑ [Search Tweets: Standard search APIレファレンス](https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets)より）
![4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508374/f0757c28-cdad-74b8-6706-c071212602da.png)
（↑ [Search Tweets: Premium search APIsレファレンス](https://developer.twitter.com/en/docs/tweets/search/api-reference/premium-search)より）

検索クエリが入るパラメータの名前が、無償版では「`q`」であるのに対し、Premium search APIsでは「`query`」です。
それ以外のパラメータも、レファレンスを見ながら書き換えました。

あと地味ですが、吐き出されるjsonの内部構造の名前もちょっと違いましたので書き換えてます。

###③コード
以前のコードがベースとなっているので、以前のコードのベースとなっているこちらを再度引用させていただきます。ありがとうございます。

>[twitter APIで遊んでみる #3(検索結果の取得)](https://qiita.com/tomozo6/items/d7fac0f942f3c4c66daf)


```test.py
#! python3
# -*- coding: utf-8 -*-

import json
from requests_oauthlib import OAuth1Session

# OAuth認証部分
CK      = '取得したConsumer key'
CS      = '取得したConsumer secret'
AT      = '取得したAccess token'
ATS     = '取得したAccess token secret'
twitter = OAuth1Session(CK, CS, AT, ATS)

# Twitter Endpoint(検索結果を取得する)
url = 'https://api.twitter.com/1.1/tweets/search/fullarchive/development.json'

# Enedpointへ渡すパラメーター
keyword = '"ピクミン"'

params ={
         'query' : keyword ,  # 検索キーワード
         'maxResults': 20 ,   # 取得するtweet数
         'fromDate' : 201301311500 ,
         'toDate' : 201302011500 
        }

req = twitter.get(url, params = params)

if req.status_code == 200:
    res = json.loads(req.text)
    for line in res['results']:
        print(line['text'])
        print('*******************************************')
else:
    print("Failed: %d" % req.status_code)

```
`'fromDate'`, `'toDate'`は任意のパラメータですが、これらはUTCで指定します。日本時間ではないです。 
###④結果
このコードを回した結果と、Twitter検索で「"ピクミン" since:2013-02-01_00:00:00_JST until:2013-02-02_00:00:00_JST」と[検索した結果](https://twitter.com/search?q=%22%E3%83%94%E3%82%AF%E3%83%9F%E3%83%B3%22%20since%3A2013-02-01_00%3A00%3A00_JST%20until%3A2013-02-02_00%3A00%3A00_JST&src=typed_query&f=live)を比較してみました。
![6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508374/7b0e47ec-a154-612c-0989-97d521b8e3ff.png)
どちらか一方にのみ表示されているツイートを赤枠で囲んでみました。
APIによる検索結果でのみ表示されるのは、「RT @ XXXX :」から始まるツイートです。当時の公式RTなのでしょうか……？
一方で、APIによる検索では表示されなかったツイートもありました。こちらについてはもうわけわからんです。なんでや。
何かご存知の方、ご教授いただければ幸いです。
（コードが悪いのか、APIでの検索結果で一番最後がダブって表示されているのはいったん置いておいてます）

##多分卒論は書ける
さて、「APIでの検索結果に表示されないがTwitter検索の結果に表示される」ツイートがあるのはなぜかはわかりませんが、Twitter検索での「最新」に表示される検索結果は、リツイート以外すべて表示されているようです（両方に表示されないツイートがある可能性は否定されませんが……）。
ので、Twitter検索をベースとした定量的分析に一定の妥当性はあると考えている次第です。
何とか首の皮がつながったので、この調子で頑張って卒論を書きたいと思います。(=ﾟωﾟ)ﾉ

何か情報があれば、教えていただけますと幸いです。
まだまだ初心者ですので、至らない点がありましたらご指摘ください。