雑記: Tweepy による Twitter API v2 の利用

概要

Twitter API をつかおうとして Twitter Developer アカウントを作成して Project と App を作成すると思います。このとき色々なトークン等が画面上に出てきてどれが何に必要なのかわからないと思います。なので App の Keys and tokens タブにある ID/トークン/キー/シークレットと認証方法との対応をまとめると下の表になります [1]。

認証方法必要なID/トークン/キー/シークレット備考
〈1〉OAuth 2.0 Bearer Token 認証Bearer TokenApp を作成すると勝手に画面に出てくる。
〈2〉OAuth 1.0a User Context 認証API Key and Secret
Access Token and SecretApp の Keys and tokens タブで明示的に Generate をクリックして生成する必要がある(はず)。
〈3〉OAuth 2.0 Authorization Code Flow with PKCE (User Context) 認証Client IDUser authentication settings ※ を設定し終わると生成される。認証方法〈2〉を用いる場合であっても App を Read and write 以上の権限にしたい(Ex. ツイートをしたい)なら User authentication settings で権限を設定する必要があるので注意。

Twitter API を叩くのがもっぱら自分であれば Type of App は Automated App or bot とし、App permissions は適切な権限にし、Redirect URL と Website URL は何でもよいはずである(私は自分のツイッターアカウントのホーム画面にした)。
Client Secret

ではどの認証方法にすればいいのかということになりますが、Twitter API を叩くのがもっぱら自分であれば〈2〉、Twitter API を用いたアプリケーションを一般に公開するようなときは〈3〉になると思います。〈1〉でできることは少ないと思いますがこれで用が足りるなら〈1〉だと思います。操作の一部の例と認証方法の対応表は以下です [2] [3] [4] [5]。

操作 Twitter API v2 のインターフェースである tweepy.Client のメソッド 〈1〉〈2〉〈3〉
ツイートID(ブラウザ上でそのツイートのみを表示したときの URL の末端)からツイート内容を引く .get_tweet(ツイートID, user_auth=True/False) ⭕️⭕️⭕️
自分(App 作成者)のアカウントのユーザIDを引く .get_me() ⭕️⭕️
ユーザIDからその人のツイートを引く .get_users_tweets(ユーザID, user_auth=True/False) ⭕️※⭕️⭕️
自分(App アクセス者)のアカウントでツイートする .create_tweet(text='ツイート文章', user_auth=True/False) ⭕️⭕️
※ ただしユーザIDを引くのがこの認証ではできない。

参考文献

  1. Authentication — tweepy 4.9.0 documentation(2022年5月8日参照).
  2. Client — tweepy 4.9.0 documentation(2022年5月8日参照).
  3. GET /2/tweets/:id | Docs | Twitter Developer Platform(2022年5月8日参照).
  4. GET /2/users/me | Docs | Twitter Developer Platform(2022年5月8日参照).
  5. GET /2/users/:id/tweets | Docs | Twitter Developer Platform(2022年5月8日参照).
  6. POST /2/tweets | Docs | Twitter Developer Platform(2022年5月8日参照).


前提

以下では Python から各認証方法で実際に Twitter API を叩くコードを示しますが、そもそも Twitter Developer アカウントを生成して Project と App を作成しトークンなどを取得しておいてください(どのトークンがどこで出現するかはこの記事冒頭の表を参照)。また、手元の Python 環境に Tweepy をインストールしておいてください。

import tweepy

# 以下は App を作成する過程で勝手に生成されて画面に出てくる
API_KEY = 'xxxxxx'  #〈2〉
API_KEY_SECRET = 'xxxxxx'  #〈2〉
BEARER_TOKEN = 'xxxxxx'  #〈1〉

# 以下は作成した App の Keys and tokens タブで Access Token and Client を Generate すると生成される
ACCESS_TOKEN = 'xxxxxx'  #〈2〉
ACCESS_TOKEN_SECRET = 'xxxxxx'  #〈2〉

# 以下は User authentication settings を設定し終わると生成される
CLIENT_ID = 'xxxxxx'  #〈3〉
CLIENT_SECRET = 'xxxxxx'  #〈3〉


〈1〉OAuth 2.0 Bearer Token 認証の例

OAuth 2.0 Bearer Token 認証では以下のような操作ができます。ただし「ユーザIDからその人のツイートを引く」については、ユーザIDがこの認証では取得できないので〈2〉や〈3〉の認証で取得する必要があります。なお、ユーザIDとは Twitter 上で表示される「名前」や「ユーザー名(@xxx)」ではなく、API でしか取得できない ID です。

# OAuth 2.0 Bearer Token 認証情報付きのアクセスクライアント
client_oath_20_bearer_token = tweepy.Client(
    bearer_token=BEARER_TOKEN
)

# ツイートID(ブラウザ上でそのツイートのみを表示したときの URL の末端)からツイート内容を引く
# これは OAuth 2.0 Bearer Token 認証でできる
print('\n◆ ツイートIDからツイート内容を引く')
resp = client_oath_20_bearer_token.get_tweet('1522958148683788288')
print(type(resp.data))
print('id:', resp.data.id)
print('text:', resp.data.text)

# ユーザIDからその人のツイートを引く
# これは OAuth 2.0 Bearer Token 認証でできる(ただしユーザIDを引くのがこの認証ではできない)
print('\n◆ ユーザIDからその人のツイートを引く')
resp = client_oath_20_bearer_token.get_users_tweets(my_user_id, max_results=5)
print(type(resp.data), type(resp.data[0]))
print(len(resp.data), '件のツイートを取得しました')
for i, tweet in enumerate(resp.data[:3]):
    print(f'----- {i + 1} -----')
    print(tweet.data)
print('以下略')
◆ ツイートIDからツイート内容を引く
<class 'tweepy.tweet.Tweet'>
id: 1522958148683788288
text: 今日のクッキペディアです
https://t.co/2vWaGbvWIP

◆ ユーザIDからその人のツイートを引く
<class 'list'> <class 'tweepy.tweet.Tweet'>
5 件のツイートを取得しました
----- 1 -----
{'id': '1523153322991316992',
 'text': 'PKCE test'}
----- 2 -----
{'id': '1523135051642601472',
 'text': '昼食に鶏もも肉をつかわなければならなくてチキンピザは昨日つくりました'}
----- 3 -----
{'id': '1522958148683788288',
 'text': '今日のクッキペディアです\nhttps://t.co/2vWaGbvWIP'}
以下略


〈2〉OAuth 1.0a User Context 認証の例

OAuth 1.0a User Context 認証では先ほどの操作に加えてユーザIDの取得やツイートができます。ただしツイートするにはあらかじめ User authentication settings を設定しておくことが必要になります。また、先ほどの Bearer Token 認証でも可能であった操作については、メソッドの引数で明示的に user_auth=True として、トークン認証ではなく OAuth 1.0a ユーザ認証をさせないと失敗します(ただしこれは現実的にはアクセスクライアントにどちらの認証情報も設定しておけば済む話です)。

# OAuth 1.0a User Context 認証情報付きのアクセスクライアント
client_oath_10a_user_context = tweepy.Client(
    consumer_key=API_KEY,
    consumer_secret=API_KEY_SECRET,
    access_token=ACCESS_TOKEN,
    access_token_secret=ACCESS_TOKEN_SECRET
)

# ツイートID(ブラウザ上でそのツイートのみを表示したときの URL の末端)からツイート内容を引く
print('\n◆ ツイートIDからツイート内容を引く')
resp = client_oath_10a_user_context.get_tweet('1522958148683788288', user_auth=True)
print(type(resp.data))
print('id:', resp.data.id)
print('text:', resp.data.text)

# 自分のアカウントのユーザID(数字列でありアットマークの後ろの文字列ではない)を引く
# これは User Context 認証が必要である
print('\n◆ 自分のアカウントのユーザIDを引く')
resp = client_oath_10a_user_context.get_me()
print(type(resp.data))
# print('id:', resp.data.id)  # Twitter Developer に登録した本人しか許可されないので秘密
print('name:', resp.data.name)
my_user_id = resp.data.id  # 自分のユーザIDを取得

# ユーザIDからその人のツイートを引く
print('\n◆ ユーザIDからその人のツイートを引く')
resp = client_oath_10a_user_context.get_users_tweets(
    my_user_id, max_results=5, user_auth=True
)
print(type(resp.data), type(resp.data[0]))
print(len(resp.data), '件のツイートを取得しました')
for i, tweet in enumerate(resp.data[:3]):
    print(f'----- {i + 1} -----')
    print(tweet.data)
print('以下略')
    
# 自分のアカウントでツイートする
# これは User Context 認証が必要である
# かつあらかじめ User authentication settings が必要である
print('\n◆ 自分のアカウントでツイートする')
if True:
    resp = client_oath_10a_user_context.create_tweet(
        text='昼食に鶏もも肉をつかわなければならなくてチキンピザは昨日つくりました'
    )
    print(type(resp.data))
    print(resp.data)
◆ ツイートIDからツイート内容を引く
<class 'tweepy.tweet.Tweet'>
id: 1522958148683788288
text: 今日のクッキペディアです
https://t.co/2vWaGbvWIP

◆ 自分のアカウントのユーザIDを引く
<class 'tweepy.user.User'>
name: クッキー

◆ ユーザIDからその人のツイートを引く
<class 'list'> <class 'tweepy.tweet.Tweet'>
5 件のツイートを取得しました
----- 1 -----
{'id': '1523153322991316992',
 'text': 'PKCE test'}
----- 2 -----
{'id': '1523135051642601472',
 'text': '昼食に鶏もも肉をつかわなければならなくてチキンピザは昨日つくりました'}
----- 3 -----
{'id': '1522958148683788288',
 'text': '今日のクッキペディアです\nhttps://t.co/2vWaGbvWIP'}
以下略

◆ 自分のアカウントでツイートする
<class 'dict'>
{'id': '1523135051642601472',
 'text': '昼食に鶏もも肉をつかわなければならなくてチキンピザは昨日つくりました'}


〈3〉OAuth 2.0 Authorization Code Flow with PKCE (User Context) 認証の例

OAuth 2.0 Authorization Code Flow with PKCE (User Context) 認証でも OAuth 1.0a User Context 認証のときと同じ操作ができますが、操作の度に以下の手順を踏んでトークンを得る必要があります。

  • 認証画面の URL を生成する。
  • ブラウザでその URL にアクセスして、App が現在ログイン中のアカウントにアクセスすることを許可する。
  • リダイレクト先の URL をパラメータごとコピーして fetch_token に渡し、有効期限付きのトークンを得る。

リダイレクト先の URL を得るまで Python でできるかもしれないですが面倒なのでやっていません。以下のスクリプトは input で標準入力を待機します。なお、以下の redirect_url に "https://twitter.com/CookieBox26" を設定しても URL パラメータが付いていないのでトークンはもらえません。パラメータ付きのリダイレクト URL であっても、異なる OAuth2UserHandler が生成した認証画面のリダイレクト先であった場合はやっぱりトークンはもらえません。

なお、この認証方法の場合は、先ほどとは逆に user_auth=False として OAuth 1.0a ユーザ認証ではなくトークン認証にさせないと失敗します。

作成した App を個人利用する限りにおいてはこの認証方法をとる必要はないのではと思っています。

# 認証ハンドラを作成する
oauth2_user_handler = tweepy.OAuth2UserHandler(
    client_id=CLIENT_ID,
    redirect_uri="https://twitter.com/CookieBox26",  # 自分で設定したリダイレクト先 URL
    scope=["tweet.read", "users.read", "tweet.write"],  # 何を許可するか
    client_secret=CLIENT_SECRET  # only necessary if using a confidential client
)
# まず認証画面の URL を生成する
auth_url = oauth2_user_handler.get_authorization_url()
# 上の URL にアクセスして認証を許可してリダイレクト先の URL を(パラメータ付きで)改めて以下に入力する
print(auth_url)
redirect_url = input("Enter Redirect URL: ")
# 適切なパラメータ付きのリダイレクト先の URL を渡したとき一時的なトークンがもらえる
token = oauth2_user_handler.fetch_token(redirect_url)
print(token)
# アクセスクライアントを生成する
client_oath_20_user_context = tweepy.Client(bearer_token=token['access_token'])

print('\n◆ ツイートIDからツイート内容を引く')
resp = client_oath_20_user_context.get_tweet('1522958148683788288', user_auth=False)
print(type(resp.data))
print('id:', resp.data.id)
print('text:', resp.data.text)

print('\n◆ App 作成者のアカウントのユーザIDを引く')
resp = client_oath_20_user_context.get_me(user_auth=False)
print(type(resp.data))
# print('id:', resp.data.id)  # Twitter Developer に登録した本人しか許可されないので秘密
print('name:', resp.data.name)
my_user_id = resp.data.id  # ユーザIDを取得

# ユーザIDからその人のツイートを引く
print('\n◆ ユーザIDからその人のツイートを引く')
resp = client_oath_20_user_context.get_users_tweets(
    my_user_id, max_results=5, user_auth=False
)
print(type(resp.data), type(resp.data[0]))
print(len(resp.data), '件のツイートを取得しました')
for i, tweet in enumerate(resp.data[:3]):
    print(f'----- {i + 1} -----')
    print(tweet.data)
print('以下略')

print('\n◆ App アクセス者のアカウントでツイートする')
if True:
    resp = client_oath_20_user_context.create_tweet(text='PKCE test', user_auth=False)
    print(type(resp.data))
    print(resp.data)
◆ ツイートIDからツイート内容を引く
<class 'tweepy.tweet.Tweet'>
id: 1522958148683788288
text: 今日のクッキペディアです
https://t.co/2vWaGbvWIP

◆ App 作成者のアカウントのユーザIDを引く
<class 'tweepy.user.User'>
name: クッキー

◆ ユーザIDからその人のツイートを引く
<class 'list'> <class 'tweepy.tweet.Tweet'>
5 件のツイートを取得しました
----- 1 -----
{'id': '1523153322991316992',
 'text': 'PKCE test'}
----- 2 -----
{'id': '1523135051642601472',
 'text': '昼食に鶏もも肉をつかわなければならなくてチキンピザは昨日つくりました'}
----- 3 -----
{'id': '1522958148683788288',
 'text': '今日のクッキペディアです\nhttps://t.co/2vWaGbvWIP'}
以下略

◆ App アクセス者のアカウントでツイートする
<class 'dict'>
{'id': '1523153322991316992',
 'text': 'PKCE test'}