TwitterAPIで特定ツイートの本文から日付を取得する方法

# やりたいこと
特定のツイートの本文中から日付文字列を抽出して、datetime型のデータとして保持
他のdatetime型と比較したりGoogleスプレッドシートに格納する。
タイトルそのまんまですね。

## 対象ツイート
https://twitter.com/assaultlily_lb/status/1511267407511367682

## 取得したい情報
本文中の「開催期間」の初日と最終日の日付情報
``` tweetest.py
datetime.datetime(2022, 4, 15, 0, 0)
datetime.datetime(2022, 4, 20, 23, 59)
```
(時分は固定値を入力しています)

## 環境
Python 3.9
tweepy
discord.py
gspread

https://qiita.com/shamimi367/items/996612e2e845fe9d1db3

ベースの動作環境はこちらのリンク先のDiscordのbotになっています。

# 作業手順
1. Twitter API(Elevated Access)の取得
1. tweepyのインポート
1. Discord Botへの組み込み

## 1. Twitter API(Elevated Access)の取得
これだけで1本記事が書けてしまうので、先人の記事の参考リンクを貼っておきます。

Developer登録 -> App作成まで

https://yosshilifeup.com/twitterapi2022/

Elevated Accessの申請方法

https://lemon818.com/twitter-api/

調べてみると、審査の完了までに数週間を要するケースもあるみたいですが、
自分の時は申請ボタンを押した瞬間に通ってました。
かなりややこしいイメージがあったので少し拍子抜けです。
(ややこしくならなくて良かった)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1138438/9f903e7e-2fde-eb40-de8e-92af9b7b900a.png)
取得成功です。

# 2. tweepyでツイートを取得してみる
本番環境(Bot)に組み込む前に、まずはローカル環境でツイートの取得を試みました。

``` tweetest.py
import tweepy

# APIキー(省略)

# キーのセット(省略)
def twitter_api():
    auth = tweepy.OAuthHandler
    return tweepy.API(auth)

# 二川二水の最新の挨拶ツイートを取得する
def futagawa():
    api = twitter_api()
    search = "こんばんは"
    user_id = "assault_lily"
    tweets = api.user_timeline(
        screen_name=user_id,
        count=100,
        include_rts=False
    )
    for tweet in tweets:
        if search in tweet.text:
            print(tweet.text)
            break
def main():
    futagawa()

if __name__ == "__main__":
    main()
```
実行結果

``` print(tweet.text)
こんばんはー二川です。
昨晩の予告通り本日も新キャラのご紹介をします＾＾　#アサルト
リリィ　#御台場女学校 https://t.co/j90el4FxuJ
```

ちなみに、この関数はこっそりと本番環境にも実装してます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1138438/c89094ad-b841-c029-0b79-15a3be6f86fd.png)

JSONデータの構成が最初いまいちよく分からなかったので、
API関係の解説サイトを見ながら手探りで探り当てていきました。
(投稿日時=created_at がちょっと曲者でした)


# 3. Discord Botへの組み込み
いよいよ本題です。
この手法が使えるのは、(良くも悪くも)毎回ツイートのテンプレートが存在する場合のみになります。
フォーマットが変わるとテキスト整形ロジックが変わってしまいます…。

・検索語句の設定
取得したい情報が確実に含まれる語句を選択
```search = "レギオンリーグ開催スケジュール"```

``` cogs.tweet.py
# レギオンリーグ日程を取得する
def get_league_by_tweet():
    api = twitter_api()
    search = "レギオンリーグ開催スケジュール"
    user_id = "assaultlily_lb"
    tweets = api.user_timeline(
        screen_name=user_id,
        count=100,
        include_rts=False,
        # tweet_mode="extended"
    )
# 続く
```

###  ハマったポイント
この状態で、tweetest.py同様```print(tweet.text)```を実行すると
``` print(tweet.text)
【🛡4/10(日) 第11回 #レギオンリーグ  日程決定🛡】

レギオンリーグをプレイしてレジェンダリーメモリアを手に入れ 
よう⭐️

▼第11回レギオンリーグ開催スケジュール
・エントリー期間
4/10(日) 17:00 ～ 4… https://t.co/cajiehxFw2
```

となり、ツイート本文が途中で切れてしまいました。
色々調べた結果、動画などが埋め込まれている場合は140文字を超えてしまう為
省略されてしまうとのこと

https://qiita.com/hitsumabushi845/items/f7fd87106381fc65fc86

tweepyのリファレンスにもご丁寧に例文付きで書かれてました。ちゃんと公式読め

https://docs.tweepy.org/en/stable/extended_tweets.html

というわけで、さっきのコードのコメントアウト部
```tweet_mode="extended"```
を追記して解決です。

``` print(text.full_text)
【🛡4/10(日) 第11回 #レギオンリーグ  日程決定🛡】

レギオンリーグをプレイしてレジェンダリーメモリアを手に入れ 
よう⭐️

▼第11回レギオンリーグ開催スケジュール
・エントリー期間
4/10(日) 17:00 ～ 4/14(木)23:59予定
・開催期間
4/15(金) ～ 4/20(水)予定
・結果発表
4/21(木) ～

#ラスバレ https://t.co/ep8xdcgvxM

```

ここまで来たら、本文を切り刻んで日付を抽出するだけです。

取得対象ツイート(確認)

https://twitter.com/assaultlily_lb/status/1511267407511367682

### 取得処理
``` tweet.py
import re # 正規表現ライブラリ

def get_league_by_tweet():
    # 続き
    for tweet in tweets:
        if search in tweet.full_text:
            # "開催期間"の前後で分割
            t1 = tweet.full_text.split("開催期間")
            # "予定"の前後で分割
            t2 = t1[1].split("予定") # '\n4/15(金) ～ 4/20(水)'   
            t3 = t2[0].split("～") # ['\n4/15(金) ', ' 4/20(水)']
            ll_st = t3[0].split("/") # ['\n4', '15(金) ']
            ll_en = t3[1].split("/") # [' 4', '20(水)']
            
            # 正規表現 \Dで数字以外を空文字に置換
            st_m = re.sub(r"\D", "", ll_st[0]) # '4'
            st_d = re.sub(r"\D", "", ll_st[1]) # '15'
            en_m = re.sub(r"\D", "", ll_en[0]) # '4'
            en_d = re.sub(r"\D", "", ll_en[1]) # '20'
            
            # datetime型のリストに変換
            dt_ll_st = dt(2022, int(st_m), int(st_d), 0, 0)
            dt_ll_en = dt(2022, int(en_m), int(en_d), 23, 59)
            dt_ll = [dt_ll_st, dt_ll_en]
            
            # リストを返す
            if len(dt_ll) != 0 and dt_ll[0] != "":
                return dt_ll
#return
[datetime.datetime(2022, 4, 15, 0, 0), 
 datetime.datetime(2022, 4, 20, 23, 59)]
```

若干冗長な処理の気もしますが、細かく刻むことで◯文字目などの指定をせずに
ざっくりと切り出せるので表記ゆれ等にも対応出来そうな気がします。

### 活用方法
1.リーグスケジュールを自動取得して更新する(1回/日)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1138438/14e66989-23de-6647-5c96-5df7df820a7e.png)
スプレッドシートに記録済のスケジュールと比較して、
新しいスケジュールを受信した時に自動的にシートの情報を更新するようにしています。
(上の定期メンテ情報も同じロジックで取得しています)

``` functions.py
# レギオンリーグ日時をスプレッドシートにセットする
import gspread

def set_league_schedule(dt_ll_list):
    try:
        worksheet.update_acell("B12", dt_ll_list[0].strftime("%Y/%m/%d %H:%M"))
        worksheet.update_acell("C12", dt_ll_list[1].strftime("%Y/%m/%d %H:%M"))
        return True
    except gspread.exceptions:
        return False
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1138438/9a1314c4-ca07-2d53-533a-dc0f9313dabe.png)
取得したデータはdatetime型なので、スプレッドシートにはstrftime関数で
str型に変換してから格納しています。

# おわりに
今回、別のライブラリを使って業務でTwitter APIを扱う機会があったので、
どうせなら何かBotに組み込んで遊べないかなと思ってたらいい感じの使い道が見つかったのでお試しで実装してみました。
最初は◯文字目の決め打ちて取ってたら日付の桁の変更に耐えられない(当たり前)だったので、少々いじってます。

TwitterAPIは単一ツイートの取得のみならず、ツイートの分析など
幅広く遊べそうなので、今後もうちょっと手を入れていきたいなと思います。