シーズンカレンダー、次戦、レース結果
Python で、「次のレース」カウントダウン付きのレースカレンダーと、ラウンドごとの結果ページを作ります。
8 分で読めます
レースアプリにはカレンダーが欠かせません。このガイドでは、レース関連のエンドポイントから 3 つのものを作ります。
- ラウンド順に並んだシーズン全体のカレンダー。
- カウントダウンやプッシュ通知用の次のレース。
- 終了したラウンドの公式結果。
カレンダー
GET /races/seasons/{year} は、1 つのカテゴリについてシーズンの全ラウンドを返します。
{
"round_number": 12,
"name": "Qatar Airways Grand Prix of Great Britain",
"circuit_name": "Silverstone Circuit, Silverstone",
"country": "United Kingdom",
"flag": "🇬🇧",
"category": "MOTOGP",
"race_date": "2026-08-09",
"season_year": 2026
}
ラウンドは順番どおりには返されないので、round_number でソートします。
import os
from datetime import date
import requests
HOST = "motogp-api-championship-data-statistics.p.rapidapi.com"
HEADERS = {"X-RapidAPI-Key": os.environ["RAPIDAPI_KEY"], "X-RapidAPI-Host": HOST}
def get(path: str, **params):
res = requests.get(f"https://{HOST}{path}", headers=HEADERS, params=params, timeout=10)
res.raise_for_status()
return res.json()["data"]
def calendar(year: int, category: str = "MOTOGP") -> list[dict]:
races = get(f"/races/seasons/{year}", category=category)
return sorted(races, key=lambda r: r["round_number"])
for race in calendar(2026):
print(f"R{race['round_number']:>2} {race['race_date']} {race['flag']} {race['name']}")
...
R 6 2026-05-17 🇪🇸 Monster Energy Grand Prix of Catalunya
...
R12 2026-08-09 🇬🇧 Qatar Airways Grand Prix of Great Britain
...
次のレース
race_date は ISO 形式の日付(YYYY-MM-DD)なので、文字列としてソート・比較することも、date.fromisoformat でパースすることもできます。
def next_race(races: list[dict], today: date) -> dict | None:
upcoming = [r for r in races if date.fromisoformat(r["race_date"]) >= today]
return upcoming[0] if upcoming else None
race = next_race(calendar(2026), date.today())
if race:
days = (date.fromisoformat(race["race_date"]) - date.today()).days
print(f"Next: {race['name']} at {race['circuit_name']} in {days} days")
else:
print("Season finished")
ラウンドの結果
ラウンドが開催されると、シーズンとラウンドを指定して結果を取得できます。
def results(year: int, round_number: int, category: str = "MOTOGP") -> list[dict]:
rows = get(f"/races/seasons/{year}/round/{round_number}/results", category=category)
return sorted(rows, key=lambda r: r["position"])
for row in results(2026, 1)[:3]:
print(f"P{row['position']} {row['rider_name']:<22} {row['team_name']:<32} +{row['points']} pts")
P1 Marco Bezzecchi Aprilia Racing +25 pts
P2 ...
P3 Raúl Fernández SuperFile Trackhouse MotoGP Team +16 pts
各行には status があり、完走したライダーは FINISHED、それ以外は DNF や DNS などの値になります。完走できなかったライダーは、完走者の下に表示してください。
レース後のポーリング
結果はレースウィークエンドごとに追加されます。「結果が出ました」という通知を送る場合は、数秒ごとではなく、レース日以降 30〜60 分ごとに最新ラウンドの結果エンドポイントをポーリングしてください。空のリストは「まだ」を意味し、ゆっくりしたポーリングならクォータに十分収まります。カレンダーにないラウンドは 404 を返します。
def results_ready(year: int, round_number: int) -> bool:
return len(results(year, round_number)) > 0
Moto2 と Moto3
上記のどの関数にも category="MOTO2" または category="MOTO3" を渡せます。ラウンド番号と日付は同じカレンダーに従うため、週末をまとめたビューは round_number でのマージで作れます。