L'Ultimate Fighting Championship publie une vaste archive de données publiques de combats sur ufcstats.com : chaque événement terminé, chaque combat, et une décomposition par combattant des frappes, des takedowns et des résultats. Pour quiconque construit un modèle de prédiction de combats, un jeu de données pour la recherche sur les paris, ou une simple référence « qui a battu qui », cette page publique de statistiques est une mine d'or, et copier des chiffres manuellement cesse de fonctionner dès que vous vous intéressez à plus d'un seul événement.

Ce guide montre la meilleure façon de scraper les statistiques UFC avec Python : vous récupérerez les pages de statistiques rendues via la Crawling API, parserez les tableaux avec BeautifulSoup, et assemblerez un enregistrement propre pour chaque combattant et combat. La procédure complète reste limitée aux statistiques sportives publiques (noms de combattants, records, résultats et métriques de combat). Elle ne touche pas aux réseaux sociaux, aux données personnelles, ni à quoi que ce soit derrière une connexion, et la section légalité vers la fin n'est pas du remplissage, alors lisez-la avant de pointer ceci sur un volume réel.

Ce que vous allez construire

Un script Python qui prend une URL publique de combattant ou d'événement ufcstats.com, récupère le HTML rendu via la Crawling API, et extrait un enregistrement structuré. Nous utiliserons la page de détails publics d'un combattant comme exemple fil conducteur et extrayons ces champs :

  • Fighter name le combattant auquel la page appartient, par exemple « Jon Jones ».
  • Record la ligne de victoires-défaites-nuls de carrière, par exemple « 27-1-0 ».
  • Event and date l'événement auquel chaque combat listé appartient, avec sa date.
  • Opponent l'autre combattant dans chaque combat.
  • Result le résultat du point de vue de ce combattant : victoire, défaite ou nul.
  • Key stats les frappes significatives et les takedowns enregistrés pour le combat.

Pourquoi une simple requête peine sur ufcstats.com

Vous pouvez parfois récupérer le balisage statique de ufcstats.com avec un simple client HTTP, mais un scraper brut se heurte à deux problèmes récurrents sur tout site sportif public. Premièrement, la mise en page repose sur des tableaux dont les lignes se chargent avec des scripts et du style, de sorte qu'une récupération naïve peut vous donner une page partielle ou une coquille allégée à laquelle manquent les colonnes de statistiques que vous cherchiez. Deuxièmement, les sites surveillent le trafic automatisé : un flux de requêtes depuis une seule IP datacenter, frappant toutes le même chemin dans une boucle serrée, est limité en taux ou contesté bien avant que vous ayez fini une saison d'événements.

Un scraper UFC fonctionnel a donc besoin de deux choses en une seule requête : une récupération qui retourne la page entièrement formée, et une IP que le site lit comme un visiteur normal. Vous pouvez construire cela vous-même avec un navigateur sans interface graphique plus un pool de proxies résidentiels rotatifs, mais les assembler et les maintenir en bonne santé représente l'essentiel du travail. La Crawling API regroupe les deux en un seul appel : vous lui envoyez l'URL, elle récupère la page derrière une IP de confiance, et retourne le HTML terminé pour que vous le parser.

When to render

Crawlbase propose deux types de tokens. Le token normal récupère le HTML statique ; le token JavaScript (JS) rend d'abord la page dans un vrai navigateur. De nombreuses pages ufcstats.com retournent leurs tableaux dans le HTML statique, donc commencez avec le token normal. Si une colonne de statistiques revient vide, passez au token JS et le balisage rendu inclura le contenu chargé tardivement.

Prérequis

Vous avez besoin de quelques éléments en place avant d'écrire du code. Aucun ne prend longtemps.

Python de base. Vous devez être à l'aise pour écrire et exécuter un script Python et installer des packages avec pip. Si vous êtes nouveau dans le parsing HTML, le guide BeautifulSoup en Python couvre le travail de sélecteur que ce tutoriel suppose.

Python 3.8 ou ultérieur. Confirmez votre version avec python --version. Si vous ne l'avez pas, installez-le depuis python.org ou via une distribution comme Anaconda.

Un compte Crawlbase et un token. Inscrivez-vous, ouvrez votre tableau de bord, et copiez votre token depuis la page de compte. Traitez le token comme un mot de passe : il authentifie vos requêtes, donc gardez-le hors du contrôle de version.

Configurer le projet

Créez un environnement virtuel pour que les dépendances du projet restent isolées, puis installez les deux bibliothèques dont le scraper a besoin.

bash
python --version

python -m venv ufc_env
source ufc_env/bin/activate

pip install crawlbase beautifulsoup4

Sur Windows, activez l'environnement avec ufc_env\Scripts\activate à la place de la ligne source. Deux dépendances font le travail : crawlbase est le client officiel pour la Crawling API, et beautifulsoup4 parse le HTML retourné pour que vous puissiez extraire des champs individuels par sélecteur CSS.

Étape 1 : Récupérer la page de statistiques rendue

Commencez par obtenir la page terminée. Importez la classe CrawlingAPI, initialisez-la avec votre token, et demandez l'URL du combattant. Vérifier le code de statut avant de parser rend les échecs bruyants plutôt que silencieux.

python
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    response = api.get(page_url)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    page_url = "http://www.ufcstats.com/statistics/events/completed"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

Cela reprend le snippet minimal de la procédure originale, modernisé pour le client officiel : au lieu de construire manuellement l'URL de requête et d'encoder vous-même l'URL cible, vous passez l'URL de la page à api.get et laissez le client gérer l'encodage et le token. Exécutez le script avec python scraper.py et vous devriez voir du vrai balisage de statistiques, le tableau des événements terminés, pas une coquille vide. Cela confirme que la récupération fonctionne avant d'écrire un seul sélecteur. Remplacez l'URL d'exemple par une URL de détails de combattant comme http://www.ufcstats.com/fighter-details/<id> pour cibler la page d'un combattant.

Crawlbase Crawling API

Ce seul appel api.get fait plus qu'il n'y paraît. Derrière lui, la Crawling API récupère la page ufcstats.com via une IP résidentielle rotative de confiance et vous remet le HTML terminé, vous permettant d'éviter de faire tourner une flotte de navigateurs sans interface graphique et de maintenir un pool de proxies juste pour lire un tableau de statistiques public. Pointez-la vers une page de combattant sur le niveau gratuit d'abord.

Étape 2 : Analyser les champs des combattants avec BeautifulSoup

Avec le HTML en main, chargez-le dans BeautifulSoup et extrayez chaque champ. Une page de combattant ufcstats.com dispose le nom et le record de carrière en haut, puis liste chaque combat dans un tableau de résultats où chaque ligne porte l'adversaire, l'événement, la date, l'issue et les métriques par combat. Enveloppez l'extraction dans de petits assistants pour qu'un champ manquant ne fasse pas planter l'exécution.

python
from bs4 import BeautifulSoup

def text_of(node):
    return node.get_text(strip=True) if node else None

def scrape_fighter(html):
    soup = BeautifulSoup(html, "html.parser")

    name = text_of(soup.select_one(".b-content__title-record"))
    record = text_of(soup.select_one(".b-content__title-record"))

    bouts = []
    rows = soup.select("tr.b-fight-details__table-row")
    for row in rows:
        cells = row.select("td.b-fight-details__table-col")
        if len(cells) < 10:
            continue
        fighters = [text_of(p) for p in cells[1].select("p")]
        opponent = fighters[1] if len(fighters) > 1 else None
        strikes = [text_of(p) for p in cells[4].select("p")]
        takedowns = [text_of(p) for p in cells[5].select("p")]
        event = [text_of(p) for p in cells[6].select("p")]

        bouts.append({
            "result": text_of(cells[0]),
            "opponent": opponent,
            "sig_strikes": strikes[0] if strikes else None,
            "takedowns": takedowns[0] if takedowns else None,
            "event": event[0] if event else None,
            "date": event[1] if len(event) > 1 else None,
        })

    return {"name": name, "record": record, "bouts": bouts}

L'assistant text_of fait une chose utile : il retourne le texte épuré d'un nœud, ou None quand le nœud est absent, de sorte qu'un appel .get_text() ne lève jamais d'exception sur rien. La page du combattant regroupe le nom et le record victoires-défaites-nuls dans le même bloc de titre, c'est pourquoi les deux champs lisent depuis .b-content__title-record et vous séparez le nom du record en aval. Chaque ligne de résultats vit dans un tr.b-fight-details__table-row, et ses cellules suivent un ordre stable : résultat, les deux combattants, KO, frappes significatives, takedowns, puis l'événement et la date. Indexer les cellules par position maintient le parser lisible tout en extrayant le résultat, l'adversaire, les statistiques clés et l'événement pour chaque combat. Si vous voulez un rappel sur le choix de sélecteurs robustes, le guide sur XPath et les sélecteurs CSS est un bon complément.

Selectors drift

Les noms de classe b-content__title-record et b-fight-details__table reflètent le balisage actuel de ufcstats.com. Les sites publics se redesignent sans préavis, donc traitez les sélecteurs ci-dessus comme un modèle de départ, pas un contrat. Quand un champ revient None, réinspectez la page en direct dans les outils de développement de votre navigateur et mettez à jour le sélecteur. La maintenance périodique des sélecteurs est normale pour tout scraper en production, pas le signe que quelque chose est cassé.

Étape 3 : Tout assembler

Assemblez maintenant la récupération et le parsing en un seul script fonctionnel. Récupérez le HTML, passez-le au parser, et affichez l'enregistrement structuré.

python
import json
from crawlbase import CrawlingAPI
from bs4 import BeautifulSoup

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def crawl(page_url):
    response = api.get(page_url)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def text_of(node):
    return node.get_text(strip=True) if node else None

def scrape_fighter(html):
    soup = BeautifulSoup(html, "html.parser")
    name = text_of(soup.select_one(".b-content__title-record"))
    record = text_of(soup.select_one(".b-content__title-record"))

    bouts = []
    for row in soup.select("tr.b-fight-details__table-row"):
        cells = row.select("td.b-fight-details__table-col")
        if len(cells) < 10:
            continue
        fighters = [text_of(p) for p in cells[1].select("p")]
        strikes = [text_of(p) for p in cells[4].select("p")]
        takedowns = [text_of(p) for p in cells[5].select("p")]
        event = [text_of(p) for p in cells[6].select("p")]
        bouts.append({
            "result": text_of(cells[0]),
            "opponent": fighters[1] if len(fighters) > 1 else None,
            "sig_strikes": strikes[0] if strikes else None,
            "takedowns": takedowns[0] if takedowns else None,
            "event": event[0] if event else None,
            "date": event[1] if len(event) > 1 else None,
        })

    return {"name": name, "record": record, "bouts": bouts}

def main():
    page_url = "http://www.ufcstats.com/fighter-details/f4c49976c75c5ab2"
    html = crawl(page_url)
    if not html:
        return
    data = scrape_fighter(html)
    print(json.dumps(data, indent=2))

if __name__ == "__main__":
    main()

À quoi ressemble le résultat

Exécutez le script complet avec python scraper.py et vous obtenez un enregistrement structuré propre pour le combattant, prêt à écrire en JSON, CSV ou dans une base de données. Les chiffres exacts dépendent du combattant et du combat ; la forme reste la même.

json
{
  "name": "Jon Jones",
  "record": "Record: 27-1-0",
  "bouts": [
    {
      "result": "win",
      "opponent": "Ciryl Gane",
      "sig_strikes": "9",
      "takedowns": "1",
      "event": "UFC 285: Jones vs. Gane",
      "date": "Mar. 04, 2023"
    }
  ]
}

Passer à l'échelle sur de nombreux combattants et événements

Une page de combattant est une démonstration ; un vrai jeu de données en parcourt beaucoup. La forme reste la même : gardez une liste d'URLs de combattants, récupérez chacune via la Crawling API, parsez-la avec la même fonction, et collectez les lignes. Comme chaque page de combattant partage la même structure de tableau, le parser que vous avez déjà écrit fonctionne sur toutes sans modifications. Un schéma courant est de commencer depuis l'index des événements terminés, de parcourir la liste des combats de chaque événement, et de suivre les liens de combattants depuis là, puis d'écrire tout à la fin.

python
import time

fighters = [
    "http://www.ufcstats.com/fighter-details/f4c49976c75c5ab2",
    "http://www.ufcstats.com/fighter-details/07f72a2a7591b409",
]

results = []
for url in fighters:
    html = crawl(url)
    if html:
        results.append(scrape_fighter(html))
    time.sleep(2)

with open("ufc_stats.json", "w") as f:
    json.dump(results, f, indent=2)

L'appel time.sleep entre les requêtes est délibéré. Même si la Crawling API fait tourner les IP pour vous, cadencer maintient votre exécution polie et prévisible, et donne à chaque page le temps de revenir sans empiler les requêtes les unes sur les autres. Pour transformer les enregistrements par combattant en tableau plat, bouclez sur la liste bouts et écrivez une ligne CSV par combat avec le nom du combattant attaché ; le même schéma de récupération puis parsing s'étend aux pages d'événements si vous préférez indexer par événement plutôt que par combattant.

Rester non bloqué

Même sur un site public de statistiques, un flux de requêtes automatisées peut être limité en taux. Quelques habitudes maintiennent une exécution saine, et elles s'appliquent à tout site que vous scrapez à grande échelle.

  • Cadencez vos requêtes. Marteler les pages dans une boucle serrée est le moyen le plus rapide de se faire limiter en taux. Répartissez les requêtes et variez vos cibles au lieu de crawler un seul chemin à pleine vitesse.
  • Misez sur la rotation. Un pool d'IP résidentielles répartit les requêtes sur de nombreuses adresses d'utilisateurs réels de sorte qu'aucune ne déclenche une limite de taux. La Crawling API gère cela pour vous ; si vous montez votre propre stack, c'est la partie à bien faire.
  • Lisez les codes de statut. Une exécution qui commence à retourner des défis ou des erreurs vous dit que le taux actuel ou le niveau d'IP n'est plus suffisant. Traitez cela comme un signal pour reculer, pas du bruit à ignorer.

Pour le guide complet, voir l'analyse approfondie sur scraper des sites web sans se faire bloquer et le guide général pour scraper un site web avec Python. Si vous préférez router votre propre trafic via un pool rotatif plutôt que d'utiliser l'API gérée, le Smart AI Proxy (aussi appelé AI Proxy) vous donne la même rotation d'IP résidentielles comme endpoint proxy standard.

Est-il légal de scraper les statistiques de l'UFC ?

Le fait de scraper les statistiques UFC est autorisé dépend des conditions d'utilisation du site de statistiques, de votre juridiction, et de ce que vous faites avec les données. Le scraping d'informations publiques que tout le monde peut voir sans compte est généralement sur un terrain plus sûr que la collecte de données protégées ou personnelles, mais « public » n'est pas une autorisation générale. Lisez les conditions d'utilisation et le robots.txt de tout site sur lequel vous pointez ceci, y compris ufcstats.com, et traitez les deux comme la limite de ce que vous collectez et à quelle vitesse. Aucun code ici ne change ces conditions ; il fait juste fonctionner la partie technique.

Restez dans un périmètre étroit. Ce guide est délibérément limité aux statistiques publiques de combat et de combattants : noms, records victoires-défaites-nuls, événements et dates, résultats, et métriques par combat comme les frappes significatives et les takedowns. Il ne couvre pas les données des réseaux sociaux, les données personnelles sur les combattants ou les fans, le contenu derrière une connexion, ni toute tentative de contourner l'authentification, et il ne redistribue pas de médias protégés par le droit d'auteur comme les images d'événements ou les photos. Ceux-là sont hors périmètre ici intentionnellement, car ce sont les statistiques publiques qui maintiennent le travail défendable. Si vous êtes tenté d'élargir le périmètre aux publications sociales ou aux profils personnels, c'est exactement là que ce tutoriel s'arrête.

Pour tout usage commercial, préférez une source officielle ou sous licence. L'UFC et ses partenaires de données accordent des licences de statistiques de combats via des flux officiels, et des fournisseurs plus larges de données sportives proposent des API payantes avec des droits d'utilisation clairs et un schéma stable que vous n'avez pas à redécouvrir chaque fois qu'une page est redesignée. Scraper la page publique de statistiques est le bon outil pour la recherche, un projet personnel, ou un jeu de données ponctuel là où aucun flux sous licence ne couvre votre besoin ; quand vous construisez un produit sur ces données, une API de données sportives sous licence est la voie plus propre et plus sûre.

Récapitulatif

Points clés

  • Récupérez via une IP de confiance. Une requête directe vers ufcstats.com peut retourner une page partielle ou être limitée en taux ; la Crawling API la récupère derrière une IP résidentielle rotative en un seul appel.
  • Parsez les tableaux avec BeautifulSoup. La page du combattant expose le nom et le record dans un bloc de titre et chaque combat dans un b-fight-details__table-row, donc indexez les cellules pour extraire le résultat, l'adversaire, les frappes, les takedowns et l'événement.
  • Attendez-vous à ce que les sélecteurs évoluent. Les noms de classe b-content__title-record et de tableau reflètent le balisage actuel ; réinspectez et mettez-les à jour quand un champ retourne None.
  • Montez en charge en bouclant les URLs avec cadençage. Le même parser fonctionne sur chaque page de combattant, donc un vrai jeu de données est une liste de liens plus un court délai entre les requêtes.
  • Restez sur les statistiques publiques, sous licence pour un usage commercial. Limitez-vous aux statistiques publiques de combat et de combattants, respectez les CGU et robots.txt, et préférez un flux de données sportives officiel ou sous licence quand vous construisez un produit.

Foire aux questions

Quelle est la meilleure façon de scraper les statistiques UFC avec Python ?

Récupérez la page publique de statistiques via la Crawling API pour obtenir le HTML complet derrière une IP rotative de confiance, puis parsez-le avec BeautifulSoup. Cette combinaison gère les deux parties difficiles : la récupération qui retourne la page complète et une IP que le site lit comme un visiteur normal, pendant que vous vous concentrez sur la sélection des champs que vous voulez dans les tableaux.

Quelles statistiques UFC puis-je extraire ?

Depuis une page publique de combattant ou d'événement, vous pouvez extraire le nom du combattant, le record victoires-défaites-nuls, l'adversaire de chaque combat, l'événement et la date, le résultat, et les métriques par combat comme les frappes significatives et les takedowns. Ce guide reste sur les statistiques sportives publiques uniquement ; il ne couvre pas les réseaux sociaux, les données personnelles, ni rien derrière une connexion.

Ai-je besoin du token normal ou du token JS pour ufcstats.com ?

Commencez avec le token normal. De nombreuses pages ufcstats.com retournent leurs tableaux dans le HTML statique, donc le token normal est généralement suffisant. Si une colonne de statistiques revient vide, passez au token JavaScript (JS), qui rend la page dans un vrai navigateur avant de restituer le HTML, et le contenu chargé tardivement sera présent quand vous parserez.

Mes sélecteurs renvoient None. Qu'est-ce qui a changé ?

Presque certainement le balisage du site. Les noms de classe comme b-content__title-record et les lignes b-fight-details__table changent quand une page est redesignée, de sorte que des sélecteurs qui fonctionnaient le mois dernier peuvent se casser. Réinspectez une page en direct dans les outils de développement de votre navigateur et mettez à jour les sélecteurs. La maintenance périodique des sélecteurs est normale pour tout scraper en production.

L'UFC propose-t-il une API officielle de statistiques ?

L'UFC et ses partenaires de données accordent des licences de statistiques de combats via des flux officiels, et des fournisseurs plus larges de données sportives vendent des API payantes avec des droits d'utilisation clairs et un schéma stable. Pour tout usage commercial, préférez l'une de ces sources sous licence. Scraper la page publique de statistiques est préférable pour la recherche, un projet personnel, ou un jeu de données ponctuel là où aucun flux sous licence ne couvre votre besoin.

Comment éviter d'être bloqué en scrapant les statistiques de l'UFC ?

Maintenez un faible taux de requêtes par IP, variez vos cibles au lieu de boucler sur un seul chemin, et routez via des IP résidentielles rotatives pour qu'aucune adresse ne déclenche une limite de taux. La Crawling API gère la rotation et un pool d'IP de confiance pour vous ; si vous montez votre propre stack, c'est la partie dans laquelle investir. Surveillez les codes de statut et reculez quand vous commencez à voir des défis.

Commencer à construire

Crawlez n'importe quel site à grande échelle, sans combattre l'infrastructure.

Crawlbase gère les proxies, les empreintes et les CAPTCHA afin que votre équipe livre des pipelines de données au lieu de maintenir la plomberie de crawl. 1 000 requêtes gratuites, sans carte requise.

En libre-service · Sans appel commercial requis · Volumes de crawl entreprise disponibles