はじめに
どうも,Heroku無料枠廃止のニュースに戦々兢々としているyagaodekawasuです。
友人と作ったTRPGの補助ツール「Shinobi-Mas」について友人がTwitterで宣伝したところ,思いの外拡散されました。
せっかく伸びたので,RT先でどのように言及されているか調べて今後の開発の参考にしようと思ったんですが,RTした人全員のTweetを手動で見に行くのは限界があると思い,tweepyを使ってTwitter APIを叩いて自動収集できるようにしました。【お知らせ】
— あべのひらが奈 (@AndNatzummy__) August 25, 2022
秘匿ハンドアウト有りのTRPGシステム向けのハンドアウト管理ツール「Shinobi-Mas」を公開します。
ハンドアウトの一覧管理と、テーブル形式での秘密所持状況の確認・更新ができます。
是非使ってみてください。
▼Shinobi-Mashttps://t.co/FNRvMvRT3R#TRPG #シノビガミ #インセイン pic.twitter.com/oUTFFKslLA
コードはGitHubにおいてあります。
注意
今回作成したスクリプトは,ある程度伸びが落ち着いた状態でTweetの全件取得を1回行うというものです。今まさに伸びているTweetの反響をリアルタイムで収集するためにはもっと色々工夫が必要になると思います。
また,無料で使えるTwitter APIでは7日以上前のTweetを取得することができません。その点もお気をつけください。
前提条件
今回のコードを動かすためには,以下の条件を満たす必要があります。
- Twitter DeveloperサイトでTwitterAPI用のAppを作成し、APIキーを取得
- アクセストークンキーとシークレットキーを取得
- Elevatedにレベルをあげる
- Pythonの実行環境を用意して
pandas
,pytz
,tweepy
をインストールする
1, 2のやりかたはこちら
【Twitter】APIキー利用申請から発行までの手順解説|ツイッター運用自動化アプリ作成に向けた環境構築
3のやりかたはこちら
【2022年度最新版】Twitter API 申請方法(Twitter v1.1 v2対応)
ポイント
APIクライアント作成
api = tweepy.API(auth, wait_on_rate_limit=True)
APIクライアント作成時に,オプションでwait_on_rate_limit=True
を指定しています。
これはTwitter APIの実行回数制限(今回の検索APIは15分間に180回まで)を超えてしまった場合,異常終了せずに一定時間スリープして,再びAPIを叩けるようになったら処理を再開してくれるので,自分でエラーハンドリングを考える必要がなくなるという便利なやつです。
ターミナル上にも下のようにメッセージが出るので安心です。
Rate limit reached. Sleeping for: 860
RTのみを検索
ret = api.search_tweets(q="「Shinobi-Mas」を公開 filter:nativeretweets", count=100, max_id=max_id)
RT先で何を言われているか調べるには誰がRTしたか特定する必要があり,そのためにはRTの一覧が必要です。
幸い,Search APIのクエリには(引用を含まない純粋な)RTだけを取得するfilter:nativeretweets
というコマンドがあるので,それを使いましょう。
ループ処理
max_id = -1
while True:
# 初回以外は,前回取得した最後のTweetのID - 1をmax_idとする
if max_id != -1:
max_id -= 1
ret = api.search_tweets(q="「Shinobi-Mas」を公開 filter:nativeretweets", count=100, max_id=max_id)
# 検索結果が0件になったら終了
if len(ret) == 0:
break
for tweet in ret:
# 何かしらの処理
# max_idの更新
max_id = ret[-1].id
これは下のサイトで紹介されているコードをtweepy
用に書き換えたものになります。max_id
を更新することによって,前のAPI呼び出しで取得した100件より古い次の100件を取得することができます。
TwitterAPIを用いたクローラー作成 | DATUM STUDIO株式会社
search_tweets
の戻り値はTweetオブジェクトのリスト(新→旧の順)になっているので,len
で検索結果の数を調べられるし,IDや本文などTweetの属性へのアクセスも簡単です。比べてみるとtweepy
の便利さがわかりますね。
検索期間の設定
since = tweet.created_at.strftime("%Y-%m-%d_%H:%M:%S_UTC")
until = (tweet.created_at + timedelta(minutes=10)).strftime("%Y-%m-%d_%H:%M:%S_UTC")
RTしたユーザーがRT直後に何を呟いているか調べるために,検索期間を指定します。
大抵の場合,RTしたTweetについて言及するなら10分以内には呟くだろうという想定で,今回は「RTした時刻から10分」という範囲で指定します。
Tweetオブジェクトのcreated_at
属性はdatetime
型(UTC)になっているので,timedelta
で調整しつつstrftime
でstr
に変換することができます。
検索クエリのsince
やuntil
はタイムゾーンを指定しなければ自動的にUTCと解釈されるとは思いますが,念のため末尾に_UTC
を付けています。このへんはお好みで。
タイムスタンプの整形
#関数: UTCをJSTに変換する
def change_time_JST(u_time):
#イギリスのtimezoneを設定するために再定義する
utc_time = datetime(u_time.year, u_time.month,u_time.day, \
u_time.hour,u_time.minute,u_time.second, tzinfo=timezone.utc)
#タイムゾーンを日本時刻に変換
jst_time_since = utc_time.astimezone(pytz.timezone("Asia/Tokyo"))
# 文字列で返す
str_time_since = jst_time_since.strftime("%Y-%m-%d_%H:%M:%S")
return str_time_since
# 中略
# Tweet日時(JST)
ctime = change_time_JST(ret2[-1].created_at)
RT直後のTweetを取得してCSVに書き込む前に,Tweet作成時刻をJSTの見やすい形に整えます。
この関数は以下のサイトで紹介されているものを使用させていただきましたので,詳しい解説はそちらをご覧ください。
【コピペするだけ!】Pythonでツイート情報を取得する方法【初心者でも簡単】
CSVへの書き込み
# CSVに書き込む用のマスターDataFrame
df = pd.DataFrame({}, columns=['name', 'screen_name', 'text', 'created_at'])
while True:
# 中略
for tweet in ret:
# 中略
# 1行のDataFrameを作成してマスターに結合
df2 = pd.DataFrame({'name': [user.name], 'screen_name': [user.screen_name], 'text': [text], 'created_at': [ctime]})
df = pd.concat([df, df2])
# DataFrameをCSVに出力
df.to_csv('out.csv', index=False)
今回はpandasのDataFrame
を生成し,それをCSVに出力するという方法でやってみました。
最初にマスターを作成して,そこに1行ずつ追加していくんですが,リストのようにappend
でどんどん追加していくということがDataFrame
ではできず,別のDataFrame
を作ってマスターにconcat
で結合するという方式を採りました。
pandasに詳しくないので,これがベストなのかわかりません。もっといいやり方があったら教えてください。
実行結果
スクリプトを実行して出力されたCSVをExcelで読み込むとこんな感じになります(元Tweetに関係ある部分だけ表示してます)。
約800RTに対して,出力はおよそ160行。そのうち元Tweetに関係ありそうなものは34件でした。だいたい4%ってところですかね。
好意的な意見が多くて一安心です。
まとめ
tweepyは便利です。
最後までご覧いただきありがとうございました。
参考
API — tweepy 4.10.1 documentation
Tweet object | Docs | Twitter Developer Platform
Twitterの検索で時間を含む期間指定をする方法 - Qiita
Twitter API(Tweepy) でのレート制限をハンドリングする方法|Tweepy Rate limit エラーで困ったら - Compass note
pandas.DataFrameに列や行を追加(assign, appendなど) | note.nkmk.me
pandasでDataFrameをcsvファイルに書き出す(to_csv):インデックスのありなしに注意しよう - Irohabook
[Twitter API] リツイートを100件より多く取得する方法 – プログラミング生放送