Python
mastodon

PythonのrequestsでmastodonのAPIを叩く

More than 1 year has passed since last update.


はじめに

【追記】Streaming APIの触りも書きました → PythonのrequestsでmastodonのStreamingAPIを叩く

Mastodon API の叩き方 をPythonでやってみました。

なお、 Mastodon.py というのがすでにあるんですが、お気に入り画像とってきたくて favorites() メソッドを使おうとしたら引数を全く受け付けてなかったので、じゃあ自分で書くかということでちょっと書いてみました。


必要なライブラリのインストール

上の3つが必要なんですが、一番下だけインストールすればあとは勝手に付いてきます。venv環境作ってインストールしましょう

環境作って

$ python3 -m venv venv

$ . ./venv/bin/activate

pipでインストール

$ pip install requests_oauthlib

なお、以下の例はすべて、ホスト: pawoo.net という前提で書きます。


OAuth2 クライアント登録

登録用の関数を用意します。

def register_app(client_name, host, redirect_uris='urn:ietf:wg:oauth:2.0:oob', scopes='read write follow'):

data = {
'client_name': client_name,
'redirect_uris': redirect_uris,
'scopes': scopes,
}
resp = requests.post("https://{host}/api/v1/apps".format(host=host), data=data)
resp.raise_for_status()
return resp.json()

これは元記事のとおり、単純に必要なデータをPOSTするだけです。

上記はスコープが読み書きフォロー全て出来るようになっていますが、例えば読み取り専用にしたい場合は scopes='read' のようにしてください。

また、 resp.raise_for_status() についてですが、これは400,500番台のエラーだった場合にraiseしてくれるメソッドです。これ呼んで何も起きなければ、おそらく200で返ってきてるはずなので resp.json() でJSON(をPythonの辞書化したもの)を取り出します。

関数の使用例です。

>>> d = register_app('myapp', host='pawoo.net')

>>> d
{'id': 1234, 'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'client_id': 'XXXXXXXXXX', 'client_secret': 'YYYYYYYYY'}

Mastodon.pyはこのclient_idとclient_secretをファイルに書き出すとかやってくれるみたいですが、その辺は必要に応じて実装してください。


アクセストークン取得

続いてアクセストークンの取得ですが、元記事のように grant_typepassword の場合は oauthlibLegacyApplicationClient を使用します。

def fetch_token(client_id, client_secret, email, password, host, scope=('read', 'write', 'follow')):

token_url = "https://{host}/oauth/token".format(host=host)
client = LegacyApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
token = oauth.fetch_token(token_url=token_url, username=email, password=password,
client_id=client_id, client_secret=client_secret, scope=scope)
return token

client_id, client_secret はクライアント登録したときに取得したものを使用します。oauth.fetch_token() の引数 username にはMastodonのWebでログインする時同様、usernameではなくてメールアドレスを渡さないといけない点に注意してください。

また、スコープについて、クライアント登録のときに 'read write follow' で全権限を与えましたが、token取得の際も再びスコープを指定しないと read 権限しかもらえません。面倒ですがもう一度指定します。ちなみに今回は文字列のリストで渡します。

以下、使用例です。

>>> client_id = 'さっき取得したclient_id'

>>> client_secret = 'さっき取得したclient_secret'
>>> email = 'mastodonアカウントのメールアドレス'
>>> password = 'mastodonアカウントのパスワード'
>>> host = 'pawoo.net'
>>> token = fetch_token(client_id, client_secret, email, password, host)
>>> token
{'access_token': 'ZZZZZZZZZZ', 'token_type': 'bearer', 'scope': ['read', 'write', 'follow'], 'created_at': 1492356574}


API アクセス

こんな感じのクラスを用意します。

class Mstdn:

def __init__(self, token, scheme='https', host='pawoo.net'):
self.scheme = scheme
self.host = host
self.session = requests.Session()
self.session.headers.update({'Authorization': 'Bearer ' + token['access_token']})

def _build_url(self, path):
return urlunsplit([self.scheme, self.host, path, '', ''])

def _request(self, method, url, data=None, params=None):
kwargs = {
'data': data or {},
'params': params or {}
}
resp = self.session.request(method, url, **kwargs)
resp.raise_for_status()
return resp

def home_timeline(self):
url = self._build_url('/api/v1/timelines/home')
return self._request('get', url)

def toot(self, status):
url = self._build_url('/api/v1/statuses')
return self._request('post', url, data={'status': status})

アクセストークンはheaderにセットします。request.Session()でsessionオブジェクトを作って、そのheaderにセットすると同一セッション間で共有できます。

URLの組み立てに今回は urllib.parse.urlunsplit を使ってみましたがこの辺はお好みで。

こんな感じで使います。

>>> # fetch_token() 関数で取得した辞書

>>> token
{'access_token': 'ZZZZZZZZZZ', 'token_type': 'bearer', 'scope': ['read', 'write', 'follow'], 'created_at': 1492356574}
>>> # これを渡す
>>> mstdn = Mstdn(token)
>>> r = mstdn.toot("テスト")
>>> r.json()
{'id': 12345, ..., 'content': '<p>テスト</p>', ...}

home_timeline とかは引数に max_id とか指定できるはずなので改良が必要ですがひとまずこの辺で。


おわりに

requests 便利ですね。

コード全体は gist に置いてあります。