Rassemblez toutes les statistiques du PGA Tour


En tant que personne qui aime écrire et enquêter sur les ensembles de données, et en tant que grand fan de golf (et écrivain d’un blog de golf, Golf on the Mind), quand j’ai réalisé que le site Web de la PGA Tour avait une tonne de statistiques sur les joueurs de la PGA Tournée remontant au début des années 80, je me suis dit qu’il y avait certainement des recherches à faire. Et la première étape, comme pour toute analyse de données, consiste à obtenir les données sous une forme standard et utilisable. Et en réalité, vous constaterez que cet effort prend la plupart de votre temps si vous faites ce genre de chose.

Donc, avant de pouvoir commencer à regarder quelque chose d’intéressant, je dois faire du grattage. Cet article vous guidera à travers ce processus.

MISE À JOUR – 25/05/18 – Je reçois beaucoup trop de questions sur la disponibilité des données, alors j’ai revu et mis à jour le code et je le gratte actuellement chaque semaine. Je ne vais pas publier le lien ici, mais envoyez-moi un e-mail et je peux continuer et partager les liens.

Étape 1 – Téléchargement des fichiers HTML

Le processus habituel de raclage consiste à saisir la page html, à extraire les données de cette page, à stocker ces données. Répétez l’opération pour autant de pages Web contenant les données souhaitées. Dans ce cas, cependant, je voulais faire quelque chose de différent. Au lieu de saisir les données d’une page Web et de stocker ces données, je voulais réellement stocker le fichier html lui-même dans un premier temps. Ce n’est qu’après que j’aurais pu obtenir les informations de cette page.

Le raisonnement derrière cela était d’éviter de frapper inutilement les serveurs de pgatour.com. Sans aucun doute, lors du grattage, vous rencontrerez des erreurs dans votre code – soit des données manquantes, des données au format étrange que vous n’avez pas prises en compte, ou toute autre erreur aléatoire qui peut se produire. Lorsque cela se produit, et que vous devez revenir en arrière et récupérer la page Web, vous perdez du temps en saisissant le même fichier sur Internet et en utilisant des ressources de ce côté du serveur. Ni un bon résultat.

Donc, dans mon cas ici, j’ai écrit du code pour télécharger et enregistrer le html une fois, puis je peux extraire les données à ma guise de ces fichiers sans passer par Internet. Maintenant pour les détails.

Sur pgatour.com, la page de base des statistiques se trouve sur pgatour.com/stats.html. Si vous remarquez en haut. Cela vous amènera à la page d’aperçu, mais vous pouvez remarquer en haut qu’il y a huit catégories de statistiques: Off the Tee, Approach the Green, Around the Green, Putting, Scoring, Streaks, Money / Finishes et Points / Rankings. Sous chacune de ces catégories se trouve une liste de statistiques dans un tableau. En cliquant sur l’un de ces liens, vous obtiendrez les statistiques de l’année en cours pour tous les joueurs qualifiés. Sur le côté, vous remarquerez une liste déroulante dans laquelle vous pouvez sélectionner l’année pour laquelle vous souhaitez la statistique. Notre objectif est d’obtenir les pages pour chacune de ces statistiques, pour chaque année offerte, et d’enregistrer la page dans un répertoire nommé pour la statistique, et l’année comme nom de fichier.

Le modèle d’URL lorsque vous êtes sur une seule statistique est simple. Par exemple, l’URL de la distance de conduite actuelle est http://www.pgatour.com/stats/stat.101.html, et l’url de la distance de conduite en 2015 est http://www.pgatour.com/stats/stat. 101.2015.html. Il suffit d’injecter l’année dans l’URL après l’ID statistique pour obtenir ce dont vous avez besoin.

Afin d’obtenir les différentes statistiques de la page de catégorie, nous allons boucler les catégories, extraire l’URL et le nom d’une statistique, récupérer la page actuelle, voir pour quelles années la statistique est proposée, générer les URL requises et boucle ces URL en sauvegardant la page! La lecture du code devrait rendre cela plus logique.

Le dernier problème avec la saisie des pages html est le temps que cela prend. En fin de compte, nous parlons de plus de 100 statistiques, avec environ 15 à 20 ans d’histoire. Au début, je voulais jouer bien pour ne pas submerger les serveurs de pgatour.com, mais ensuite j’ai réalisé que pgatour.com peut probablement gérer la charge car ils doivent être en mesure de gérer le rafraîchissement constant que les gens font lors de la vérification des classements à la fin d’un tournoi. Heureusement, la bibliothèque Gevent de python nous permet de saisir facilement et en parallèle des pages et de les enregistrer. Après toutes ces explications, jetez un œil au code que j’ai utilisé pour enregistrer les fichiers.

url_stub = "http://www.pgatour.com/stats/stat.%s.%s.html" #stat id, year
category_url_stub = 'http://www.pgatour.com/stats/categories.%s.html'
category_labels = ['RPTS_INQ', 'ROTT_INQ', 'RAPP_INQ', 'RARG_INQ', 'RPUT_INQ', 'RSCR_INQ', 'RSTR_INQ', 'RMNY_INQ']
pga_tour_base_url = "http://www.pgatour.com"
def gather_pages(url, filename):
 print filename
 urllib.urlretrieve(url, filename)

def gather_html():
 stat_ids = []
 for category in category_labels:
 category_url = category_url_stub % (category)
 page = requests.get(category_url)
 html = BeautifulSoup(page.text.replace('n',''), 'html.parser')
 for table in html.find_all("div", class_="table-content"):
   for link in table.find_all("a"):
     stat_ids.append(link['href'].split('.')[1])
 starting_year = 2015 #page in order to see which years we have info for
 for stat_id in stat_ids:
   url = url_stub % (stat_id, starting_year)
   page = requests.get(url)
   html = BeautifulSoup(page.text.replace('n',''), 'html.parser')
   stat = html.find("div", class_="parsys mainParsys").find('h3').text
   print stat
   directory = "stats_html/%s" % stat.replace('/', ' ') #need to replace to avoid
   if not os.path.exists(directory):
     os.makedirs(directory)
   years = []
   for option in html.find("select", class_="statistics-details-select").find_all("option"):
     year = option['value']
     if year not in years:
       years.append(year)
   url_filenames = []
   for year in years:
     url = url_stub % (stat_id, year)
     filename = "%s/%s.html" % (directory, year)
     if not os.path.isfile(filename): #this check saves time if you've already downloaded the page
       url_filenames.append((url, filename))
     jobs = [gevent.spawn(gather_pages, pair[0], pair[1]) for pair in url_filenames]
     gevent.joinall(jobs)

Étape 2 – Convertissez HTML en CSV

Maintenant que j’ai les fichiers html pour chaque statistique, je veux passer par le processus d’obtention des informations des tables en html, dans un format csv consommable. Heureusement, le html est très bien formaté pour que je puisse réellement utiliser les informations. J’ai enregistré tous les fichiers html dans un répertoire appelé stats_html, et je veux essentiellement créer la même structure de dossiers dans un répertoire de premier niveau que j’appelle stats_csv.

Les étapes de cette tâche sont 1) lire les fichiers, 2) utiliser Beautiful Soup, extraire les en-têtes de la table, puis toutes les lignes de données et 3) écrire ces informations sous forme de fichier csv. Je vais juste aller directement au code car c’est aussi le plus facile à comprendre.


for folder in os.listdir("stats_html"):
 path = "stats_html/%s" % folder
 if os.path.isdir(path):
   for file in os.listdir(path):
   if file[0] == '.':
     continue #.DS_Store issues
   csv_lines = []
   file_path = path + "/" + file
   csv_dir = "stats_csv/" + folder
   if not os.path.exists(csv_dir):
     os.makedirs(csv_dir)
   csv_file_path = csv_dir + "/" + file.split('.')[0] + '.csv'
   print csv_file_path
   if os.path.isfile(csv_file_path): #pass if already done the conversion
     continue
   with open(file_path, 'r') as ff:
     f = ff.read()
     html = BeautifulSoup(f.replace('n',''), 'html.parser')
     table = html.find('table', class_='table-styled')
     headings = [t.text for t in table.find('thead').find_all('th')]
     csv_lines.append(headings)
     for tr in table.find('tbody').find_all('tr'):
       info = [td.text.replace(u'xa0', u' ').strip() for td in tr.find_all('td')]
     csv_lines.append(info)
     #write the array to csv
     with open(csv_file_path, 'wb') as csvfile:
       writer = spamwriter = csv.writer(csvfile, delimiter=',')
       for row in csv_lines:
         writer.writerow(row)

Et c’est tout pour le grattage! Il y a encore quelques problèmes avant de pouvoir réellement utiliser les données, mais ces problèmes dépendent de ce que vous essayez de découvrir. Le grand exemple étant d’obtenir l’information importante de la csv. Certaines statistiques sont des statistiques basées sur un pourcentage, d’autres sont des distances mesurées en yards. Il existe également des statistiques mesurées en pieds / pouces (23’3 ″ par exemple). Un problème est également que, parfois, la statistique souhaitée se trouve dans une colonne différente du fichier csv en fonction de l’année de la statistique. Mais comme je l’ai dit, ces problèmes ne concernent pas un article sur le grattage des données, mais nous devrons les traiter lors de la consultation des données plus tard.