startgg API, pysmashgg 使い方と使用例まとめ
この記事では、主にスマブラ大会を管理・運営するウェブサイトのstart.gg(旧smash.gg)のAPIと、そのAPI用のpythonパッケージであるpysmashggを取り上げ、使い方と使用例を解説する。大会データの取得や処理の参考にしてほしい。
トークン取得
start.gg APIを利用するには、まずstart.ggの開発者用サイトでトークンを取得しなければならない。上記の開発者用サイトにアクセスして取得しよう。なお、トークンは1年ごとに取得し直す必要がある。行き詰まったらこちらの公式サイトを参考にすること。
△自分のDeveloper Settings画面。2年前に取得したAPIは既に期限切れで、新しく取得し直している
Developer Portal
使用例
Developer Portalでクエリを実行すると、start.ggから大会データを取得できる。トークン取得後、下記サイトにアクセスしよう。
https://dev.start.gg/explorer/
以下のような画面が出る。日本語文字は自分が説明用に追加した。
まずは画面左上のクエリ入力箇所に適当なクエリを入力する。今回は試しに篝火#10の大会情報を取得するため、以下のクエリを入力する。"$slug"は大会IDにあたる文字列型の変数であり、後述の変数タブにて代入を行う。
query TournamentQuery($slug: String!) { tournament(slug: $slug) { id name countryCode addrState city isOnline startAt numAttendees } }
画面左下の変数タブ下の欄に以下を入力する。これで変数"$slug"に大会IDを代入できる。なお、"10-kagaribi-10"は篝火#10のslug(大会ID)である。slugはstart.ggの大会ページURLの"tournament"以下に記載されている。
https://www.start.gg/tournament/10-kagaribi-10/details
{ "slug": "10-kagaribi-10" }
さらに、画面左下の"Headers"をクリックしてヘッダータブに切り替え、ヘッダータブ下の欄に以下を入力する。ただし、"xxxxxxxxxxxxxxxxx"は自分のトークンに書き換える。
{ "Authorization": "Bearer xxxxxxxxxxxxxxxxx" }
最後に画面中央上のクエリ実行ボタンを押す。画面右側に以下のような表示が出れば成功。篝火#10の大会情報を取得できた。
他のクエリの例は以下の公式サイトにある。
https://dev.start.gg/docs/examples/queries/event-standings
各オブジェクトの詳細を知りたいときは以下のサイトを参照。
https://developer.start.gg/reference/event.doc.html
pysmashgg
色々クエリを試せば分かるが、Developer Portalでのデータ収集は非常に手間がかかる。たとえば大会出場者のデータを取得する場合、大量の出場者ページ1つ1つに対してクエリ実行をしなければならない。また、取得したデータを取りまとめたり、分析環境に移したりするのにも一手間かかる。pythonを使えるなら、対話型実行環境のJupyter Labで、start.gg APIに特化したパッケージのpysmashggを使うのがオススメ。
以降はpythonを使用できる人向けの解説となる。
導入
インストールにはpipを使う。Jupyter labでコマンド実行するなら先頭に"!"をつけるのを忘れずに。
pip install pysmashgg
使用例
初期設定
最初にimportとトークンの設定をする。ただし、"xxxxxxxxxxxxxxxxx"は自分のトークン文字列に書き換える。
smashgg_token = "xxxxxxxxxxxxxxxxx"
import pysmashgg smash = pysmashgg.SmashGG(smashgg_token, True) # 2番目の変数はオートリトライの有無
あとはパッケージの公式ドキュメントを参考に必要なデータを取得すればよい。
篝火#10の大会情報・出場者データ取得
たとえば篝火#10の大会情報なら、前述したslug(10-kagaribi-10)と、tournament_showメソッドを用いて以下のようにする。出力はdict形式で与えられる。
smash.tournament_show('10-kagaribi-10')
{'id': 539061,
'name': '篝火#10 / KAGARIBI#10',
'country': 'JP',
'state': '東京都',
'city': '港区',
'startTimestamp': 1683327600,
'endTimestamp': 1683468000,
'entrants': 1868}
次に、篝火#10の出場者データを取得してみよう。ここで注意しなければならないのは、出場者のデータは大会そのものではなく、大会の中の「イベント」という項目に紐付けられていることだ。
篝火#10のstart.ggページで確認してみよう。左下の青枠がイベントであり、今回は大会「篝火#10 / KAGARIBI#10」のイベント「シングルス / Singles」のデータを参照する必要がある。
出場者を取得するために、まずtournament_show_event_idメソッドを用いて、「シングルス / Singles」のイベントIDを調べる。入力するslugはイベントのstart.ggページURLから分かる。
https://www.start.gg/tournament/10-kagaribi-10/event/singles
smash.tournament_show_event_id('10-kagaribi-10', 'singles')
892118
イベントIDから出場者を取得するには、event_show_entrantsメソッドを使う。イベントIDとページ番号両方の入力が必要だが、ひとまず1番目のページの出場者を出力してみよう。
smash.event_show_entrants(892118, 1)
[{'entrantId': 12882268, 'tag': 'FaZe | Sparg0', 'finalPlacement': 1, 'seed': 2, 'entrantPlayers': [{'playerId': 158026, 'playerTag': 'Sparg0'}]}, {'entrantId': 12884054, 'tag': 'ZETA | あcola', 'finalPlacement': 2, 'seed': 1, 'entrantPlayers': [{'playerId': 2691639, 'playerTag': 'あcola'}]}, ... ]
出場者データが出力された。しかし、上記はあくまで1番目のページの出場者であり、全出場者のデータを取るには全ページに対し同様の操作をしなければならない。
そこで、pandasライブラリのfrom_dictメソッドを使い、結果をデータフレームに格納しながらループ処理を行う。また、篝火のような大型大会のデータを取る際は、サーバーエラー時に処理が中断されないようtry-exceptを入れるとよい。
import pandas as pd df = pd.DataFrame() page = 1 # 開始ページ eventId = 892118 # 全ページ巡回 while 1: try: # エラー対策 d = smash.event_show_entrants(eventId, page) except: continue if len(d): df_tmp = pd.DataFrame.from_dict(d) # 空のページを対象とするとエラー df = pd.concat([df, df_tmp]) page += 1 else: break df
出場者データを格納したデータフレームを出力できた。各項目の意味は以下の通り。
- entrantId 参加者ID。イベント×参加者ごとに一意
- tag スポンサー付き表示名
- finalPlacement 順位
- seed シード順位
- playerId プレイヤーID。プレイヤーごとに一意
- playerTag 表示名
特にplayerIdはプレイヤーごとに固有の番号で、別イベントや別大会でも共通であるため、複数大会の結果を考慮するシードやランキングの計算には欠かせない。上記のデータフレームでは、playerIdとplayerTagがリストとdictに内包されていて扱いにくいので、以下の処理で取り出してしまおう。
df = (df .assign(playerId = lambda x: x.entrantPlayers.apply(lambda y: y[0]["playerId"])) .assign(playerTag = lambda x: x.entrantPlayers.apply(lambda y: y[0]["playerTag"])) .drop(columns="entrantPlayers") )
df
任意クエリ実行 日本スマブラSP大会の取得
pysmashggのメソッドは便利だが、細かい条件を指定しにくい。たとえば「日本」の大会ならtournament_show_by_countryメソッドで取得できるが、「日本」の「スマブラSP」の大会に限定して取得できるメソッドは無い(クロブラのDX大会などが出力に混ざってしまう)。また、取得したデータに欲しい項目が無いこともある。
このような場合は、直接クエリを書いて取得条件や取得項目を細かくカスタマイズしよう。pysmashggパッケージからクエリ実行に関わるrun_queryを抜き出して拝借する。
from pysmashgg.api import run_query
実行したいクエリを文字列型の変数に記入する。クエリを記述する際は前述の公式クエリ例や、公式オブジェクト解説、pysmashggのソースコードが参考になる。以下はtournament_show_by_countryメソッドのクエリを元にした、日本(countryCode: "JP")のスマブラSP(videogameIds: 1386)の大会・イベントのデータを取得するクエリである。なお完全に別ゲーを除くには、さらに後でevents[videogame][id] == 1386で絞る必要がある。
q = """ query TournamentsByCountry($page: Int!) { tournaments(query: { perPage: 32, page: $page, sortBy: "startAt desc" filter: { countryCode: "JP" videogameIds: 1386 } }) { nodes { name id slug isOnline startAt addrState events { name id numEntrants type videogame { id } } } } }"""
run_query関数を使い上記のクエリを実行する。例によってページ番号が必要だが、ひとまず1番目のページのデータを取得しよう。
page = 1 variables = {"page": page} response = run_query(q, variables, header={"Authorization": "Bearer " + smashgg_token}, auto_retry=True) response["data"]["tournaments"]["nodes"]
[{'name': '第37回クロブラ/神奈川スマブラ対戦会クロブラ/Kurobra37', 'id': 569506, 'slug': 'tournament/37-kurobra37', 'isOnline': False, 'startAt': 1693097100, 'addrState': '神奈川県', 'events': [{'name': 'Melee Singles', 'id': 946920, 'numEntrants': 0, 'type': 1, 'videogame': {'id': 1}}, {'name': 'SP 1on1トーナメント', 'id': 946919, 'numEntrants': 0, 'type': 1, 'videogame': {'id': 1386}}]}, {'name': '第11回スマサガ(東京PM大会)', 'id': 568517, 'slug': 'tournament/11-pm', 'isOnline': False, 'startAt': 1692403200, 'addrState': '東京都', 'events': [{'name': 'Project+ Singles Tournament', 'id': 945139, 'numEntrants': 6, 'type': 1, 'videogame': {'id': 33602}},
...
]
日本のスマブラSP大会データを格納したリストを取得できた(早速2番目にPM大会が混ざっているのでevents内のvideogame内のidで絞る処理が必要)。前述の出場者データと同じく、ページ番号を増やすと次の大会データを取得できる。なお、クエリでsortBy: "startAt desc"としているので開催日が新しい順に並んでおり、大抵は最初の方に未開催の大会が来ることに注意が必要(pysmashggのメソッドを使った場合も同様)。
一部項目の意味を以下補足する。
- isOnline オンラインフラグ。Falseならオフ大会
- startAt 大会開催日。UNIX時間表記
- addrState 開催都道府県
- numEntrants 参加者数
- type イベントタイプ。1がシングルス、5がチーム
なおUNIX時間は、たとえばdatetimeモジュールを使って以下のようにすれば年月日に変換できる。
from datetime import datetime datetime.fromtimestamp(UNIX_TIME).date().strftime('%Y/%m/%d')
参考サイト