← 全部指南

带生涯统计的车手资料页

按比赛号码把车手资料和生涯总数据合并成一张资料卡。

阅读约 8 分钟

车迷应用、梦幻游戏和新闻网站都需要为每位车手建一个页面。两次调用就能拿到资料卡所需的全部内容:车手本人及其生涯总数据。

两个调用

GET /riders/93
GET /riders/93/career-stats

/riders/{rider_number} 返回资料:

{
  "rider_number": 93,
  "full_name": "Marc Márquez",
  "nationality": "Spain",
  "flag": "🇪🇸",
  "birth_date": "1993-02-17",
  "height_cm": 168,
  "weight_kg": 59,
  "team_name": "Gresini Racing MotoGP",
  "image_url": null
}

/riders/{rider_number}/career-stats 返回总数据:

{
  "rider_number": 93,
  "rider_name": "Marc Márquez",
  "total_wins": 44,
  "total_podiums": 66,
  "total_points": 1302,
  "world_titles": 2,
  "total_races": 118
}

生涯总数据是根据 API 中可用的赛季计算的,并非车手的整个职业生涯。请相应标注,例如「自 2024 年起」。

并行获取两者

import os
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass
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) -> dict:
    res = requests.get(f"https://{HOST}{path}", headers=HEADERS, timeout=10)
    res.raise_for_status()
    return res.json()["data"]


@dataclass(frozen=True)
class RiderCard:
    number: int
    name: str
    flag: str
    team: str
    age: int
    wins: int
    podiums: int
    titles: int
    races: int

    @property
    def podium_rate(self) -> float:
        return self.podiums / self.races if self.races else 0.0


def age_on(birth: str, today: date) -> int:
    born = date.fromisoformat(birth)
    return today.year - born.year - ((today.month, today.day) < (born.month, born.day))


def rider_card(number: int) -> RiderCard:
    with ThreadPoolExecutor(max_workers=2) as pool:
        profile_f = pool.submit(get, f"/riders/{number}")
        stats_f = pool.submit(get, f"/riders/{number}/career-stats")
        profile, stats = profile_f.result(), stats_f.result()
    return RiderCard(
        number=profile["rider_number"],
        name=profile["full_name"],
        flag=profile["flag"],
        team=profile["team_name"],
        age=age_on(profile["birth_date"], date.today()),
        wins=stats["total_wins"],
        podiums=stats["total_podiums"],
        titles=stats["world_titles"],
        races=stats["total_races"],
    )


card = rider_card(93)
print(f"{card.flag} #{card.number} {card.name} — {card.team}, {card.age}")
print(f"{card.wins} wins, {card.podiums} podiums in {card.races} races ({card.podium_rate:.0%})")

构建索引页

资料页需要一个列表来链接:

页面端点
本赛季发车阵容GET /riders/seasons/{year}?category=MOTOGP
按国家划分的车手GET /riders/nationality/{nationality}(例如 Italy)
数据库中的所有车手GET /riders?page=1&size=200

使用 rider_number 作为 URL 别名(/riders/93/),这正是车迷会搜索的内容。

处理缺失数据

  • image_url 可能为 null。请用彩色徽章显示比赛号码,而不是显示损坏的图片。
  • 未被使用的号码会返回 404,请渲染「未找到车手」页面,而不是报错。
  • 车手有时会在赛季之间更换号码。如果你保存收藏,请连同该号码所属的赛季一起保存。
下一篇指南 → 赛季赛历、下一场比赛与结果