#!/usr/bin/env python3 """ Script pour extraire les résultats des courses - VERSION COMPLETE Sans limitation de nombre de résultats """ import os import sys import requests import logging from bs4 import BeautifulSoup from tqdm import tqdm import pandas as pd import time def extract_results_from_page(result_url): """Extraire TOUS les résultats d'une page de résultats""" try: headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' } response = requests.get(result_url, headers=headers, timeout=30) if response.status_code != 200: return [] soup = BeautifulSoup(response.content, 'html.parser') # Chercher la table des résultats tables = soup.find_all('table') results = [] for table in tables: rows = table.find_all('tr') # Chercher l'en-tête de la table headers_found = False for row in rows: th = row.find_all('th') if th: headers_text = [h.get_text(strip=True).lower() for h in th] # Vérifier si c'est une table de résultats if 'rang' in headers_text or 'place' in headers_text or 'position' in headers_text: headers_found = True break if headers_found: # Extraire TOUTES les lignes de données for row in rows: cols = row.find_all('td') if len(cols) >= 3: try: # Extraire les informations rank = cols[0].get_text(strip=True) name = cols[1].get_text(strip=True) # Vérifier que c'est bien une ligne de données if rank and name: result = { 'rang': rank, 'nom': name, 'club': cols[2].get_text(strip=True) if len(cols) > 2 else '', 'temps': cols[3].get_text(strip=True) if len(cols) > 3 else '', 'annee_naissance': cols[4].get_text(strip=True) if len(cols) > 4 else '', 'categorie': cols[5].get_text(strip=True) if len(cols) > 5 else '', 'url_resultats': result_url } results.append(result) except Exception as e: continue # 🔧 CORRECTION ICI : Retourner TOUS les résultats (pas de limite) return results except Exception as e: logging.warning(f"Erreur pour {result_url}: {e}") return [] def extract_all_results(): """Extraire tous les résultats des courses""" logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('results_extraction.log'), logging.StreamHandler() ] ) logging.info("=== EXTRACTION COMPLÈTE DES RÉSULTATS ===") logging.info("⚠️ AUCUNE LIMITE DE NOMBRE DE RÉSULTATS") # Charger les données combinées combined_file = "data/courses/courses_list_combined.csv" if not os.path.exists(combined_file): logging.error("Fichier combiné non trouvé !") return None df = pd.read_csv(combined_file, encoding='utf-8-sig') # Filtrer les courses avec des URLs de résultats df_with_results = df[df['resultats_url'].notna() & (df['resultats_url'] != '')] logging.info(f"Courses à traiter: {len(df_with_results)}") logging.info(f"Courses sans résultats: {len(df) - len(df_with_results)}") # Extraire les résultats all_results = [] with tqdm(total=len(df_with_results), desc="Extraction des résultats") as pbar: for idx, row in df_with_results.iterrows(): result_url = row['resultats_url'] course_name = row.get('nom', 'N/A') course_date = row.get('date', 'N/A') try: results = extract_results_from_page(result_url) if results: # Ajouter les informations de la course à chaque résultat for result in results: result['course_nom'] = course_name result['course_date'] = course_date result['course_lieu'] = row.get('lieu', 'N/A') all_results.extend(results) logging.info(f"[{idx+1}/{len(df_with_results)}] {len(results)} résultats pour {course_name} ({course_date})") pbar.update(1) # Attendre un peu entre les requêtes time.sleep(0.3) except Exception as e: logging.error(f"Erreur pour {course_name}: {e}") pbar.update(1) # Sauvegarder les résultats if all_results: logging.info(f"Résultats extraits: {len(all_results)}") output_dir = "data/results" os.makedirs(output_dir, exist_ok=True) output_file = os.path.join(output_dir, "results_extracted_complete.csv") results_df = pd.DataFrame(all_results) results_df.to_csv(output_file, index=False, encoding='utf-8-sig') logging.info(f"Fichier sauvegardé: {output_file}") # Statistiques logging.info("\n=== STATISTIQUES ===") logging.info(f"Résultats totaux: {len(all_results)}") # Calculer le nombre moyen de résultats par course avg_results = len(all_results) / len(df_with_results) logging.info(f"Moyenne de résultats par course: {avg_results:.1f}") # Trouver la course avec le plus de résultats results_per_course = {} for result in all_results: course = result['course_nom'] if course not in results_per_course: results_per_course[course] = 0 results_per_course[course] += 1 if results_per_course: max_course = max(results_per_course, key=results_per_course.get) min_course = min(results_per_course, key=results_per_course.get) logging.info(f"Maximum: {results_per_course[max_course]} résultats pour {max_course}") logging.info(f"Minimum: {results_per_course[min_course]} résultats pour {min_course}") return results_df else: logging.warning("Aucun résultat extrait !") return None if __name__ == "__main__": df = extract_all_results() if df is not None: logging.info("\n✅ Extraction complète terminée avec succès!") else: logging.info("\n❌ Extraction échouée")