こんにちは、ミナピピン(@python_mllover)です。
今回はTweepyで2021年に新しく公開されたTwitterAPI V2のAPI鍵を使ってツイートする方法を紹介したいと思います。(Tweepyのバージョンは4.0以上である必要があります。)
2023年からTwitter APIは有料化され、今回のユーザーのツイート(タイムライン)取得する場合はBASICプラン(100$/月)を契約している必要があります
Contents [hide]
前準備
まずライブラリをインストールしていない場合はPIPでインストールします
# 最新バージョンをインストール
$ pip install tweepy --upgrade
LOOKUPでユーザーの内部IDを確認する
Twitterでユーザーのツイートを取得するためにはユーザータイムラインをAPIを使用します。これにはまず Users lookupでユーザーの内部ID(@~~ではない)を取得する必要があります。コードは↓
# Users lookup endpointを叩く
import requests
import tweepy
username= "<@以降のアカウントID>"
bearer_token = "<自分のbearer_token(BASICに切り替えて再生成したもの)>"
url = f"https://api.twitter.com/2/users/by/username/{username}"
headers = { "Authorization": f"Bearer {bearer_token}" }
response = requests.get(url, headers=headers)
if response.status_code == 200:
user_data = response.json()["data"]
user_id = user_data["id"]
print(f"User ID for {username}: {user_id}")
else:
print(f"Error {response.status_code}: {response.text}")
TweepyでTwitterAPI_V2のユーザータイムラインAPIを叩く
上記で変数user_idに格納された対象ユーザーの内部IDを使ってusertimelineのエンドポイントを叩きます
# OAuth 2.0 Bearer Token 認証情報付きのアクセスクライアント
client_oath_20_bearer_token = tweepy.Client(
bearer_token=bearer_token
)
params = {
"expansions": "attachments.media_keys,author_id",
"tweet.fields": "created_at,text,public_metrics",
"media.fields": "media_key,url,type",
"user.fields": "name,username,description,public_metrics"
}
results = []
count = 0
lcount = 0
pagination_token =""
tweets = client_oath_20_bearer_token.get_users_tweets(user_id,
max_results=20,
tweet_fields="entities,referenced_tweets,created_at,text,public_metrics",
expansions="attachments.poll_ids,attachments.media_keys,author_id",
media_fields="media_key,url,type,variants",
user_fields= "name,username,description,public_metrics",
exclude="retweets",
)
print(tweets.data)
ツイートに関するデータはtweets.dataに格納されています
パラメーター解説
- user_id:
- 取得するユーザーのID。Twitterの各アカウントには一意のIDが割り当てられており、このIDを指定することで特定のユーザーのツイートを取得できます。
- max_results:
- 一度のリクエストで取得するツイートの最大数。この例では20と指定していますので、最新の20ツイートを取得します。
- tweet_fields:
- 取得するツイートの属性を指定します。
entities
: ツイート内のエンティティ(URLs, ハッシュタグ, メンションなど)の詳細情報。referenced_tweets
: 当該ツイートが返信や引用の場合、元となるツイートの情報。created_at
: ツイートの投稿日時。text
: ツイートの本文。public_metrics
: ツイートの公開メトリクス(いいね数、リツイート数など)。
- expansions:
- 追加の情報を取得したい場合に使用するパラメータ。
attachments.poll_ids
: ツイートに添付された投票のID。attachments.media_keys
: ツイートに添付されたメディア(画像、動画など)のキー。author_id
: ツイートしたユーザーのID。
- media_fields:
media_key
: 各メディアの一意のキー。url
: メディアのURL。type
: メディアのタイプ(画像、動画など)。variants
: 動画の場合の異なる品質やフォーマットの情報。
- user_fields:
- 取得するユーザー情報の属性を指定します。
name
: ユーザーの表示名。username
: ユーザーのスクリーンネーム。description
: ユーザープロフィールの説明文。public_metrics
: ユーザーの公開メトリクス(フォロワー数、フォロー中の数など)。
- exclude:
- 特定のタイプのツイートを結果から除外します。この例では、リツイートを結果から除外しています。
この関数を使用することで、指定されたユーザーIDのツイートとそれに関連する追加情報を取得できます。
ツイートデータをデータフレームに加工する
次にここから各データを取り出してデータフレームに変換します
import pandas as pd
import datetime
for tweet in tweets.data:
tweet_time = datetime.datetime.fromisoformat(str(tweet.created_at).replace("Z", "+00:00"))
# JSTに変換
jst_timezone = pytz.timezone('Asia/Tokyo')
tweet_time_jst = tweet_time.astimezone(jst_timezone)
# 年-月-日 時:分:秒の形式で秒まで表示
jdate = tweet_time_jst.strftime('%Y-%m-%d %H:%M:%S')
# 'date'x, 'user_name'x, 'user_id'x, 'tweet_url'x, 'tweet_id'x, 'base_tweet_id'x, 'tweet_type'x,'text'x,'favx', 'rt'x, 'link'x, 'img_url', 'movie_url'
# 基本情報
result = {
"tweet_url": f"https://twitter.com/{tweet.author_id}/status/{tweet.id}",
"user_id":username, #@~~~~
'tweet_id':tweet.id,
"date": jdate,
"text": tweet.text,
"rt": tweet.public_metrics["retweet_count"],
"fav": tweet.public_metrics["like_count"],
"img_url": None,
"img2": None,
"img3": None,
"img4": None,
"movie_url":None
}
# ユーザー情報
user = tweets.includes["users"][0]
result["user_name"] = user.name #ユーザー名
result["screen_name"] = user.username
result["profile_text"] = user.description
result["ff_count"] = user.public_metrics["followers_count"]
# リプライかどうかの判断と親ツイートのURL取得
result["tweet_type"] = None
result["base_tweet_id"] = None
try:
result["base_tweet_id"] = tweet.get("referenced_tweets", [])[0].id
result["tweet_type"] = "child"
except:
result["tweet_type"] = "parent"
result["base_tweet_id"] = tweet.id
# 外部リンク
result['link'] = None
try:
outer_url = tweet.entities["urls"][0]['expanded_url']
if "twitter.com" not in outer_url:
# print(outer_url)
result['link'] = outer_url
except:
print("リンク無し")
pass
# メディア情報
if "attachments" in tweet:
images = []
for media in tweets.includes["media"]:
if media.media_key in tweet.attachments["media_keys"] and media.type== "photo":
images.append(media.url)
# print(media.url)
elif media.type == "video":
# 動画URLは直接得られないため、動画の情報を参照するURLとなる
# print("Video Info URL:", media.url,media.variants)
for m in media.variants:
# print(m)
if m['content_type']=='video/mp4':
result["movie_url"]=m["url"].split("?")[0]
for i, img in enumerate(images):
result[f"img{i+1}_url"] = img
print(result["movie_url"])
print("ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー")
results.append(result)
df = pd.DataFrame(results)
print(df)
サンプルコード
これらをまとめると以下のようになります。下記ではループの2回目からntil_id = oldest_id
で前回取得したものより古いツイートを指定することでページネーションを加味しています
oldest_id =""
for i in range(2):
if oldest_id !="":
tweets = client_oath_20_bearer_token.get_users_tweets(user_id,
max_results=50,
tweet_fields="entities,referenced_tweets,created_at,text,public_metrics",
expansions="attachments.poll_ids,attachments.media_keys,author_id",
media_fields="media_key,url,type,variants",
user_fields= "name,username,description,public_metrics",
exclude="retweets",
until_id = oldest_id #2回目からは前のループで取得したoldest_idより古いものだけを指定
)
else:
tweets = client_oath_20_bearer_token.get_users_tweets(user_id,
max_results=50,
tweet_fields="entities,referenced_tweets,created_at,text,public_metrics",
expansions="attachments.poll_ids,attachments.media_keys,author_id",
media_fields="media_key,url,type,variants",
user_fields= "name,username,description,public_metrics",
exclude="retweets",
)
result_count = tweets.meta['result_count']
print(f"{result_count}件のツイートを取得しました")
for tweet in tweets.data:
tweet_time = datetime.datetime.fromisoformat(str(tweet.created_at).replace("Z", "+00:00"))
# JSTに変換
jst_timezone = pytz.timezone('Asia/Tokyo')
tweet_time_jst = tweet_time.astimezone(jst_timezone)
# 年-月-日 時:分:秒の形式で秒まで表示
jdate = tweet_time_jst.strftime('%Y-%m-%d %H:%M:%S')
# 'date'x, 'user_name'x, 'user_id'x, 'tweet_url'x, 'tweet_id'x, 'base_tweet_id'x, 'tweet_type'x,'text'x,'favx', 'rt'x, 'link'x, 'img_url', 'movie_url'
# 基本情報
result = {
"tweet_url": f"https://twitter.com/{tweet.author_id}/status/{tweet.id}",
"user_id":username, #@~~~~
'tweet_id':tweet.id,
"date": jdate,
"text": tweet.text,
"rt": tweet.public_metrics["retweet_count"],
"fav": tweet.public_metrics["like_count"],
"img_url": None,
"img2": None,
"img3": None,
"img4": None,
"movie_url":None
}
# ユーザー情報
user = tweets.includes["users"][0]
result["user_name"] = user.name #ユーザー名
result["screen_name"] = user.username
result["profile_text"] = user.description
result["ff_count"] = user.public_metrics["followers_count"]
# リプライかどうかの判断と親ツイートのURL取得
result["tweet_type"] = None
result["base_tweet_id"] = None
try:
result["base_tweet_id"] = tweet.get("referenced_tweets", [])[0].id
result["tweet_type"] = "child"
except:
result["tweet_type"] = "parent"
result["base_tweet_id"] = tweet.id
# 外部リンク
result['link'] = None
try:
outer_url = tweet.entities["urls"][0]['expanded_url']
if "twitter.com" not in outer_url:
# print(outer_url)
result['link'] = outer_url
except:
print("リンク無し")
pass
# メディア情報
if "attachments" in tweet:
images = []
for media in tweets.includes["media"]:
if media.media_key in tweet.attachments["media_keys"] and media.type== "photo":
images.append(media.url)
# print(media.url)
elif media.type == "video":
# 動画URLは直接得られないため、動画の情報を参照するURLとなる
# print("Video Info URL:", media.url,media.variants)
for m in media.variants:
# print(m)
if m['content_type']=='video/mp4':
result["movie_url"]=m["url"].split("?")[0]
for i, img in enumerate(images):
result[f"img{i+1}_url"] = img
print(result["movie_url"])
print("ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー")
results.append(result)
oldest_id = tweets.meta["oldest_id"]
df = pd.DataFrame(results)
print(df)
oldest_id = ""
: 最も古いツイートのIDを保存する変数を初期化します。この変数を使って、次回のAPIリクエストでどのツイート以前のものを取得するかを指定します。oldest_id = tweets.meta["oldest_id"]
で一番古いツイートのIDを確認if oldest_id != "":
:oldest_id
が空でない場合、すなわち2回目のリクエストのときの処理。tweets = client_oath_20_bearer_token.get_users_tweets(...)
:tweepy
を使用して、指定したユーザーのツイートをAPIから取得します。until_id = oldest_id
: 最も古いツイートのIDより前のツイートを取得します。
else:
:oldest_id
が空の場合、すなわち1回目のリクエストのときの処理。
上記ではループが2回なので100件取得できる想定です。また2023年10月現在 Twitter APIのBASICプランだとユーザーのツイートは月10000ツイートまで取得可能です
まあAPIの制限は大分キツイのでSeleniumを使用するのも手段の1つだと思います
関連記事:Twitterメディアダウンローダーでユーザーの画像情報を収集しよう!
注意点
ツイートするためにはDeveloper Portalの「User authentication settings」で、アプリケーションレベルのパーミッション(App permissions
)がRead and write
か「Read and write and Direct message
」でないといけません。(初期値はRead
)
加えて、APIのツイート読み取り/書き込み/DM送信の権限変更は↑のパーミッション設定を変更しただけでは反映されないので都度、apikey/apisecret/Access Token/AccessTokenSecretをrevokeもしくはregenerateで再生成する必要があるので注意です。
関連記事:【Python】Tweepyで特定ユーザーの過去ツイートをスクレイピングで自動取得する
関連記事:【Python】TwitterのDM送信をTweepyで自動化する
関連記事:【Python】TwitterAPI V2のBearer tokenを使ってツイート検索をしてみる
参考:https://qiita.com/penguinprogrammer/items/b220be0c203eaaad015a
コメント
[…] 関連記事:【Python】TweepyでTwitterAPI_V2のAPIを使ってツイートするサンプルプログラム […]
[…] 参照:【Python】TweepyでTwitterAPI_V2のAPIを使ってツイートするサンプルプログラム […]