見出し画像

【Python】 ツイートURLなどの情報取得

(2022年9月18日追記)7日間の制約ないようにできた。以下記事参考。

やりたいこと

  • 自分のハッシュタグつけたツイートの一括取得を行いたい(Noteの読書記事に一括で貼り付けるため)

結論

早速結論を書くと、

よって、「やりたいこと」は週一で実行すれば取得できるってことになる。笑

事前準備

Twitter API の利用申請

まずは Twitter API の利用申請が必要になる、ここでの詳細説明は省略する。私は以下2つのサイトを参考にした。

tweepy ライブラリのインストール

まず事前に pip のアップデートを忘れずに行うことをおすすめします。やり方はこちらのサイトを参照した。

python -m pip install --upgrade pip

つぎに目的の tweepy を以下コマンドでインストールする。こちらのサイトを参考にした。

pip install tweepy

その他いくつかの標準外のライブラリを利用しているが、各自必要に応じてインストールすること。

作成したコード

import tweepy
from pprint import pprint
from datetime import datetime,timezone
import pytz
import csv
import sys
sys.dont_write_bytecode = True  # 自作モジュール apiKeys のキャッシュ作成させない
import apiKeys

# ★必要情報入力
search    = "from:rissy_l #夜は短し歩けよ乙女"  # 検索ワード
tweet_max = 20           # 取得したいツイート数(10〜100で設定可能)

# メイン関数
def main():
    result = SearchTweets(search,tweet_max)                     # Tweet情報の取得
    rslt_new = sorted(result, key=lambda x: x['created_at'])    # 日時で昇順にソート
    pprint(rslt_new)
    writeCsv(rslt_new)                                          # csvファイルに出力
    print('本プログラムを正常終了します。')

# クライアント関数、API利用の認証を実行
def ClientInfo():
    client = tweepy.Client(bearer_token    = apiKeys.BEARER_TOKEN,
                           consumer_key    = apiKeys.API_KEY,
                           consumer_secret = apiKeys.API_SECRET,
                           access_token    = apiKeys.ACCESS_TOKEN,
                           access_token_secret = apiKeys.ACCESS_TOKEN_SECRET,
                          )
    return client

# 関数
def SearchTweets(search,tweet_max):    
    client = ClientInfo()   # 認証実施
    # 直近のツイート取得 (API仕様上過去7日分)
    tweets = client.search_recent_tweets(query = search,
                                         max_results = tweet_max,
                                         tweet_fields = ['author_id', 'created_at', 'public_metrics'],  #投稿者ID、投稿日時、いいね
                                         user_fields = 'profile_image_url',
                                         expansions = ['author_id', 'attachments.media_keys'],
                                         media_fields = 'url',)  
    results     = []
    # tweet検索結果を辞書に記載
    if tweets.data != None:
        for tweet in tweets.data:
            obj = {}
            obj["tweet_id"] = tweet.id      # Tweet_ID
            obj["text"] = tweet.text        # Tweet Content
            obj["like_count"] = tweet.public_metrics['like_count']  # いいね数
            obj["created_at"] = change_time_JST(tweet.created_at)   # 投稿日時
            obj["author_id"] = tweet.author_id
            for i in range(len(tweets.includes['users'])):
                if tweet.author_id == tweets.includes['users'][i]['id']:
                    obj['user'] = tweets.includes['users'][i]['name']
                    obj['username'] = tweets.includes['users'][i]['username']
                    obj['profile_image_url'] = tweets.includes['users'][i]['profile_image_url']
                    obj['tweetUrl'] = "https://twitter.com/" + tweets.includes['users'][i]['username'] + "/status/" + str(tweet.id)
            results.append(obj)
    else:
        results.append('')
        print("検索ワードに該当するツイートがありません。")
    
    return results

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 = utc_time.astimezone(pytz.timezone("Asia/Tokyo"))
    # 文字列で返す
    str_time = jst_time.strftime("%Y-%m-%d_%H:%M:%S")
    return str_time

def writeCsv(data):
    label = list(data[0].keys())    # 辞書のキー(ラベル)取得
    genTime = datetime.now().strftime('%Y年%m月%d日%H時%M分%S秒')
    with open(genTime + '.csv', 'w', encoding='utf8', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=label)
        writer.writeheader()
        writer.writerows(data)

if __name__ == '__main__':
    main()

コード解説

ライブラリのインポート

import tweepy
from pprint import pprint
from datetime import datetime,timezone
import pytz
import csv
import sys
sys.dont_write_bytecode = True  # 自作モジュール apiKeys のキャッシュ作成させない
import apiKeys

tweepy は今回の twitter API  を利用するためのライブラリ。
pprint は print の強化版というか、ターミナル画面への出力をきれいに整形してくれる。
datetime は時間を扱うライブラリ。
pytz はタイムゾーンを変換するため。
csv はcsvファイルに出力するため。
sys はその直下に記載しているように自作モジュールのキャッシュが作られて煩わしいため。
apiKeys は自作のAPI認証情報を記述した外部ファイル apiKeys.py をインポートするため。後述する。

検索ワードと取得ツイート数の指定

# ★必要情報入力
search    = "from:rissy_l #夜は短し歩けよ乙女"  # 検索ワード
tweet_max = 20           # 取得したいツイート数(10〜100で設定可能)

ここで検索ワードと取得ツイート数を指定する。コメントの通り10~100個で設定可能。

メイン関数

# メイン関数
def main():
    result = SearchTweets(search,tweet_max)                     # Tweet情報の取得
    rslt_new = sorted(result, key=lambda x: x['created_at'])    # 日時で昇順にソート
    pprint(rslt_new)
    writeCsv(rslt_new)                                          # csvファイルに出力
    print('本プログラムを正常終了します。')

メイン関数。好みだがいつも main()関数内で完結するように(まとめるように)している。
SearchTweets()関数については後述を参照。

rslt_new = sorted(result, key=lambda x: x['created_at']) について
これはコメントのとおり辞書型の "result" を日時情報 "created_at" で昇順にソートしている。後述するが今回取得するツイートは新しいツイートから取得され辞書に格納しているので、日時でいうと降順で辞書に格納されている。やりたいこと(Note記事にURL貼る)は降順でツイートを貼り付けたいので降順にソートしている。ただそれだけ。こちらの記事を参考にした。

pprint(rslt_new) こちらはいったんターミナルに出力しているだけ。

writeCsv(rslt_new) はcsvファイルへの出力処理。後述。

print('本プログラムを正常終了します。') は最後までプログラムが走ったことをターミナル画面でわかるようにするため、私の好みです。

API認証

# クライアント関数、API利用の認証を実行
def ClientInfo():
    client = tweepy.Client(bearer_token    = apiKeys.BEARER_TOKEN,
                           consumer_key    = apiKeys.API_KEY,
                           consumer_secret = apiKeys.API_SECRET,
                           access_token    = apiKeys.ACCESS_TOKEN,
                           access_token_secret = apiKeys.ACCESS_TOKEN_SECRET,
                          )
    return client

こちらでAPI認証を実行している。代入している値は上述したように apiKeys.py を読み込み、そこに記述している BEARER_TOKEN などを代入している。こちらの記事を参考にした。
apiKeys.py には以下を記述しているだけ。

# API情報を記入
BEARER_TOKEN        = "****************"
API_KEY             = "****************"
API_SECRET          = "****************"
ACCESS_TOKEN        = "****************"
ACCESS_TOKEN_SECRET = "****************"

ツイートの取得(今回のメインの処理)

# 関数
def SearchTweets(search,tweet_max):    
    client = ClientInfo()   # 認証実施
    # 直近のツイート取得 (API仕様上過去7日分)
    tweets = client.search_recent_tweets(query = search,
                                         max_results = tweet_max,
                                         tweet_fields = ['author_id', 'created_at', 'public_metrics'],  #投稿者ID、投稿日時、いいね
                                         user_fields = 'profile_image_url',
                                         expansions = ['author_id', 'attachments.media_keys'],
                                         media_fields = 'url',)  
  1. client = ClientInfo() # 認証実施

    1. ここで認証処理実施し、結果を取得している。

  2. tweets = client.search_recent_tweets()

    1. ここで実際にツイートを取得している。渡している引数で取得するツイートの条件と取得する情報を指定している。

    2. max_results = tweet_max,

      1. 見ての通りツイート数の指定

    3. tweet_fields = ['author_id', 'created_at', 'public_metrics'],  

      1. コメントの通り 投稿者ID、投稿日時、いいね が取得できる。

    4. user_fields = 'profile_image_url',

      1. ツイート主のプロフィール画像のURLが取得できる。

    5. expansions = ['author_id', 'attachments.media_keys'],

      1. author_id →後で利用する user や username のために必要。

      2. attachments.media_keys → 忘れた、今回は利用していない。

    6. media_fields = 'url'

      1. 忘れた、今回は利用していない。

    results     = []
    # tweet検索結果を辞書に記載
    if tweets.data != None:
        for tweet in tweets.data:
            obj = {}
            obj["tweet_id"] = tweet.id      # Tweet_ID
            obj["text"] = tweet.text        # Tweet Content
            obj["like_count"] = tweet.public_metrics['like_count']  # いいね数
            obj["created_at"] = change_time_JST(tweet.created_at)   # 投稿日時
            obj["author_id"] = tweet.author_id
            for i in range(len(tweets.includes['users'])):
                if tweet.author_id == tweets.includes['users'][i]['id']:
                    obj['user'] = tweets.includes['users'][i]['name']
                    obj['username'] = tweets.includes['users'][i]['username']
                    obj['profile_image_url'] = tweets.includes['users'][i]['profile_image_url']
                    obj['tweetUrl'] = "https://twitter.com/" + tweets.includes['users'][i]['username'] + "/status/" + str(tweet.id)
            results.append(obj)
    else:
        results.append('')
        print("検索ワードに該当するツイートがありません。")

    return results

results = [] → 結果を格納するリストの宣言
次の if tweets.data != None: は空じゃないかを確認し、空っぽだったら else で print で通知して終了。
for 文で tweets.data 内のデータを取り出す。
obj = {} で辞書型の宣言。
辞書のキーを指定しながら tweets.data からとれる情報を順次格納している、コメント参照。
次の for 文で tweets.includes 内から取れる情報を取得している。
user -> 変更できるツイッター名のほう
username -> 変更できないツイッター名のほう
profile_image_url -> ツイート主のプロフィール画像のURL
tweetUrl -> ツイートのURL。これはAPIから直接は取れないが、こちらの記事にあるようにツイートのURLは、
 https://twitter.com/[ユーザ名]/status/[ツイートID]
なので、既に取得済みの情報からURLを作成している。

処理の大枠はこちらの記事を、APIにわたすプロパティ(引数)や取得できる情報についてはこちらの記事を参考にした。

投稿日時を日本時間の文字列に変換

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 = utc_time.astimezone(pytz.timezone("Asia/Tokyo"))
    # 文字列で返す
    str_time = jst_time.strftime("%Y-%m-%d_%H:%M:%S")
    return str_time

こちらの関数はこちらのサイトをそのまま引用させていただた。

csvに書き出し

def writeCsv(data):
    label = list(data[0].keys())    # 辞書のキー(ラベル)取得
    genTime = datetime.now().strftime('%Y年%m月%d日%H時%M分%S秒')
    with open(genTime + '.csv', 'w', encoding='utf8', newline='') as f:
        writer = csv.DictWriter(f, fieldnames=label)
        writer.writeheader()
        writer.writerows(data)

辞書型をcsvに出力している。処理自体はすごく普通なのでググれば参考になる日本語記事たくさんヒットする。わたしはこちらの記事を参考にした。

おまじない

if __name__ == '__main__':
    main()

よくおまじないと聞くやつ。働きについてはググってください。

実行結果

  • ターミナル画面

***>python twGetUrl.py
[{'author_id': 1130329021,
  'created_at': '2022-09-04_15:29:52',
  'like_count': 0,
  'profile_image_url': 'https://pbs.twimg.com/profile_images/1066227566007087104/o7DmdBVN_normal.jpg',
  'text': '偏屈王 第四十八幕!舞台: 北門\n詭弁論部に桃色ブリーフ\n #夜は短し歩けよ乙女',
  'tweetUrl': 'https://twitter.com/rissy_l/status/1566312572533751808',
  'tweet_id': 1566312572533751808,
  'user': 'lisy_すてかり',
  'username': 'rissy_l'},
 {'author_id': 1130329021,
  'created_at': '2022-09-04_16:03:06',
  'like_count': 0,
  'profile_image_url': 'https://pbs.twimg.com/profile_images/1066227566007087104/o7DmdBVN_normal.jpg',
  'text': '天命、そろそろ欲しいな\n #夜は短し歩けよ乙女',
  'tweetUrl': 'https://twitter.com/rissy_l/status/1566320934986076160',
  'tweet_id': 1566320934986076160,
  'user': 'lisy_すてかり',
  'username': 'rissy_l'}]
本プログラムを正常終了します。
  • csvファイル

tweet_id,text,like_count,created_at,author_id,user,username,profile_image_url,tweetUrl
1566312572533751808,"偏屈王 第四十八幕!舞台: 北門
詭弁論部に桃色ブリーフ
 #夜は短し歩けよ乙女",0,2022-09-04_15:29:52,1130329021,lisy_すてかり,rissy_l,https://pbs.twimg.com/profile_images/1066227566007087104/o7DmdBVN_normal.jpg,https://twitter.com/rissy_l/status/1566312572533751808
1566320934986076160,"天命、そろそろ欲しいな
 #夜は短し歩けよ乙女",0,2022-09-04_16:03:06,1130329021,lisy_すてかり,rissy_l,https://pbs.twimg.com/profile_images/1066227566007087104/o7DmdBVN_normal.jpg,https://twitter.com/rissy_l/status/1566320934986076160
vscode の拡張機能 "Edit csv" で開いた画面

終わりに(感想)

初めて Note にコードの解説記事を書いてみた。自分としても振り返りになるのでよかったと思う。

また、やりたいこと大した内容じゃないし簡単かと思っていたら、TwitterAPI が v1.1 から v2 に変わっていて7日間しか取得できずだったり、tweepy も v2 対応でネットに転がってる解説記事だと古い情報で使えなかったりなど、いろいろハマった。

誰の役に立つかは分からないがURL取得したいときにどうぞ!笑

(自分宛て備忘録)本記事は以下で乱雑に記載したものを公開用に整理したもの。

この記事が気に入ったらサポートをしてみませんか?