Twitter API v2で大量(<100)のツイートを一度に取得する in PHP

利用者の目線で、Twitter API v2を使ったツイート検索の実装例をPHPを使い紹介します。
一度に100件以上取得する際のPHPの実装例の記事がなかったのでAPI自体の仕様と共に軽く紹介します。（Pythonなどは有る）
Web APIアドベントカレンダーの18日目です！昨日17日目は@sugimomotoさんの[Mock API ライブラリ WireMock を使ってみた](https://www.cdatablog.jp/entry/wiremock)です。

https://github.com/ichii731/php-examples/tree/main/twitter_v2_search

# Twitter API v2検索概要
Twitter API v2とは2020年8月にリリースされた新しいAPIです。
また、ちょうど先月の11月中旬に**v2がデフォルトのAPIになり、v1.1からの移行が推奨されれています**
## 主要PHPライブラリの対応状況
今回は主にPHPでの実装を中心にご紹介します。
最も有名なTwitter APIのライブラリである「TwittterOAuth」は**今年の7月にバージョン3.0.0を公開してv2に対応**しました。

https://github.com/abraham/twitteroauth/releases/tag/3.0.0

https://github.com/abraham/twitteroauth/issues/874

（まぁエンドポイント変えるだけなので対応しなくても自力で出来ますがせっかくabrahamさんがやってくれたので恩恵を享受しましょう）
# v2アプリを新規登録
v1.1の認証情報ではアクセスできず、**新たにv2用のアプリを作る必要があります**が、**既にDeveloper申請に合格している人は速攻発行可能**です。
Developer Portalの左カラムからv2用のアプリケーションを登録できます。
![スクリーンショット 2021-11-29 151440.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/37ac0f04-2c02-3ed5-fbbc-2e9742ae3a31.png)

ちなみにv1.1アプリの認証キーを使ってリクエストしたら、以下のように親切にv2への登録が必須ですよと登録URLと共に返却してくれます。優しい…（惚れ）

```php
["detail"]=>
string(201) "When authenticating requests to the Twitter API v2 endpoints, you must use keys and tokens from a Twitter developer App that is attached to a Project. You can create a project via the developer portal."
["registration_url"]=>
string(55) "https://developer.twitter.com/en/docs/projects/overview"
// ︙
```

# ツイート検索＋100件以上取得
## 制限と2つのエンドポイント
##### エンドポイントの種類
v1.1ではPremiumプラン等の区分がありましたがv2では利用用途によります。

- **/2/tweets/search/recent** … 過去1週間のツイートを取得
- **Full-archive search** … 全ての期間のツイートを取得可能。**学術研究の利用者限定**

研究中の大学教員・Drに限るなどの制限があり、審査も有るので殆どの人は上段を使うことになります。
##### APIのリクエスト制限
使うアプリケーション認証とユーザー認証でレートリミットが変わるのもv1.1とほぼ同じです。

|   |ユーザー認証|アプリケーション認証|
|---|---|---|
|リクエスト回数|180回 / 15分|450回 / 15分|
|1度の取得数|10～100件|10～100件|

https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent

また、v2アプリ全体で**1ヶ月あたり200万ツイートまで**という月単位での制限も存在します。制限の状況はTwitter Deleloperのアプリページから参照出来ます。
![rapture_20211218001600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/6ef40ee8-dfcf-f7a9-c32e-9811fa4df295.png)

## 実装例(サンプルコード)
以下にサンプルコードを示します。`PHP 7.x or 8.x`を使い、Composerライブラリを導入してください。

```bash
composer require abraham/twitteroauth
```

レートリミットの制限処理は通常は制限状況を取得して確認するのがいいのですが、今回は予めアクセスする回数を指定する方式で紹介します。制限の緩いベアラートークンを使用しています。
個人開発や取得が目的の場合なら、クライアント側でリクエストした回数を記録して150に行ったら止める…的な操作でも十分良いと思います。ちゃんとやりたい場合はv1.1とやり方は変わりませんのでDo It Yourself。

```php:v2_search.php
<?php
require_once "vendor/autoload.php";
use Abraham\TwitterOAuth\TwitterOAuth;

$connection = new TwitterOAuth(
    "************************", // API Key
    "************************", // API Secret
    null,
    "************************" // ベアラートークン
);
$query = "キーワード";
// ここでAPIのバージョンを2に指定
$connection->setApiVersion("2");
$params = [
    /*
    参照：https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent
    ※queryとmax_resultは必須
    */
    "query" => $query . " -is:retweet", // RTを除外したい時
    "start_time" => "2021-12-12T00:00:00+09:00",
    "end_time" => "2021-12-03T00:00:00+09:00",
    "tweet.fields" => "created_at", // 今回は追加で投稿日時を指定
    "max_results" => 100 // 10～100の間を指定
];
// $req_countにリクエストの回数を設定・$loop_countにリクエスト回数を設定
$req_count = 0;
$loop_count = 400;

for ($i = 0; $i < $loop_count; $i++) {

    /* リクエスト制限時
    余裕を持って15分より長く、450回より短く設定しています。
    ここら辺の処理は各自皆さんでうまく対処してください。
    */
    if($req_count > 400){
        echo "制限 / 15分待機";
        $loop_count = 0;
        sleep(1000000);
    }
    // v2でリクエスト
    $request = $connection->get("tweets/search/recent", $params);
    // 取得情報を処理
    foreach ($request->data as $data) {
        // ＜ここでデータの処理をする＞
    }
    echo "Success Request";
    // 100件以上ある場合
    if (isset($request->meta->next_token)) {
        echo $req_count;
        $req_count++;
        // クエリパラメータの配列にnext_tokenを追加
        $params["next_token"] = $request->meta->next_token;
    } else {
        // 無いときは終了
        break;
    }
}

```

仕組みとしては以下のとおりです。

- 取得したい条件などをクエリパラメータを連想配列`$params`として保存
- twitteroauthでリクエスト
- リクエストして帰ってくるJSONの中に**next_tokenが入っているので取得**
- next_tokenを連想配列にぶちこむ
- forとかwhileで繰り返しリクエスト

Twitter側に迷惑のかからぬよう適宜for内でsleepしても良いと思います。ちゃんと取得できてます！
![rapture_20211218004055.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/aab0ce76-57e0-4cd1-8072-0c6475dff883.png)
### パラメータについて
**パラメータや取得できる情報などはドキュメントから確認してください**。特に**開始時間・終了時間をクエリに直接ぶちこめるようになった**のはありがたいです。時間を指定する場合、日本時間にしたいなら`+09:00`を末尾に追記。

https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent

特に**tweet.fieldの部分はとてもたくさんの情報を取得できるので必見です。**デフォルトではIDと本文しか帰ってきませんよ！詳しくは↓

https://developer.twitter.com/en/docs/twitter-api/data-dictionary/object-model/tweet

## (おまけ)従来のv1.1での100件以上取得コード
最後におまけ。従来のv1.1で取得したい場合のサンプルコードも載せときます。

```v1_1.php
<?php
require_once "vendor/autoload.php";
use Abraham\TwitterOAuth\TwitterOAuth;

loop_req("#hashtags -RT", 10);

function loop_req($keyword, $loop_count)
{
    // Set API Key and AccessToken here
    global $CK, $CS, $AT, $ATS;
    $connection = new TwitterOAuth($CK, $CS, $AT, $ATS);
    $params = [
        'q' => $keyword,
        'count' => 100
    ];
    for ($i = 0; $i < $loop_count; $i++) {
        $results = $connection->get("search/tweets", $params);
        foreach ($results->statuses as $val) {
            // Write the post-acquisition process here
            $tweet_results[] = $val->text;
        }

        //Conditional branching to see if there are more tweets that can be retrieved
        if (isset($results->search_metadata->next_results)) {
            // Get max_id
            $max_id = preg_replace('/.*?max_id=([\d]+)&.*/', '$1', $results->search_metadata->next_results);
            // Add max_id to params
            $options['max_id'] = $max_id;
        } else {
            break;
        }
    }
    return $tweet_results;
}
```

## 最後にお詫び…
アドカレの予定に登録していた記事案にて「日本語の情報が一つもないAPIを紹介」と書いていたのですが、TwitterAPIの記事に変更させていただきました…
本来、**TikTokアカウントでログインできるようにするTikTok APIを解説する予定**でしたが、TikTok API自体が今年の5月にリリースされたばかりであまりにも使いみちが無い…というわけで代打としてTwitter APIのPHP実装例に変更しました。

**TikTok Login Kitを活用したログイン機能を実装したソースコードと解説**を以下のリポジトリにアップロードしましたので、もしTikTok APIに興味がありましたら併せてご覧いただければ幸いです。

https://github.com/ichii731/php-examples/tree/main/tiktok-login