Smascherare il basket usando Python
In questa serie di blog, vengono sfruttate potenti librerie Python per aiutare a scoprire alcune verità statistiche nascoste nel basket. Il primo passo in qualsiasi approccio basato sui dati è identificare e raccogliere i dati necessari.
Fortunatamente per noi, Basketball-Reference.com ospita pagine di dati sul basket che possono essere facilmente recuperate. I processi di questa procedura dettagliata possono essere facilmente applicati a qualsiasi numero delle loro pagine, ma per questo caso, abbiamo in programma di raschiare le statistiche stagionali di più classi principianti.
Gli obiettivi
- Identificare l'origine dati
- Scarica la Pagina
- Identifica gli elementi importanti della pagina
- Pre-pulire ed estrarre
- Archivio
- Pacchetto richieste
- Bellissimo pacchetto di zuppe
- Pacchetto Panda
Identificare l'origine dei dati
Basketball-Reference.com ospita centinaia di pagine curate sulle statistiche del basket che vanno dalle medie stagionali di categorie tipiche di box score come punti, rimbalzi e percentuali di tiro, fino all'azione play-by-play di ogni partita giocata nell'ultima 20 anni circa. Si può facilmente perdere la strada in questo tsunami statistico se non c'è un obiettivo chiaro su cosa cercare esattamente.
L'obiettivo qui in questo post è semplice: ottenere dati sui principianti che aiuteranno a valutare il vero valore e il potenziale di un giovane giocatore.
Il seguente link è una di queste pagine. Elenca tutte le statistiche rilevanti dei debuttanti in una particolare stagione.
https://www.basketball-reference.com/leagues/NBA_1990_rookies-season-stats.html
Per accumulare dati sufficienti per fare solide deduzioni statistiche sui giocatori, un anno di dati non basta. Sono necessari dozzine di anni di dati raccolti per aiutare a filtrare il rumore e giungere a una conclusione sul potenziale futuro di un giocatore.
Se un'azione può essere ripetuta manualmente, si rende un ottimo candidato per l'automazione. In questo caso, il numero nell'URL sopra corrisponde al rispettivo anno di quella classe principiante. Forti di questa conoscenza, iniziamo a mettere insieme le nostre prime righe di codice.
import requests
from bs4 import BeautifulSoup
import pandas as pd
years = list(range(1990, 2017))
url_base = "https://www.basketball-reference.com/leagues/NBA_{}_rookies-season-stats.html"
- I pacchetti appropriati vengono importati
- La
yearsvariabile elenco specifica l'intervallo degli anni desiderati, dal 1990 al 2017 url_baseserve a memorizzare la variabile stringa preformattata dell'URL di destinazione
Nello scraping delle pagine web, è imperativo rimuovere quanto più sovraccarico possibile. Visto che il sito memorizza tutte le loro informazioni sul front-end HTML, la pagina può essere facilmente scaricata e archiviata localmente nella sua interezza.
#loop iterates through years
for year in years:
url = url_base.format(year)
data = requests.get(url)
#each page year is locally stored as an HTML file
with open("notebooks/Rookies/{}.html".format(year), "w+") as f:
f.write(data. Text)
- Le parentesi graffe trovate all'interno della
urlstringa di serviranno per consentire aformatdi sostituirlo con l'anno correntemente iterato
— Ad esempio, nella sua prima iterazione, ilurlvalore saràhttps://www.basketball-reference.com/leagues/NBA_1990_rookies-season-stats.html
- Alla sua seconda iterazione, l'anno successivo verrebbe invece indicato:https://www.basketball-reference.com/leagues/NBA_1991_rookies-season-stats.html - La variabile di dati funge da segnaposto per la
requests.get()funzione e i riferimenti delurlvalore di stringa attualmente iterato - Il
requestsmetodo utilizza quindi la stringa URL appena formattata per recuperare la pagina in questione - Il successivo
with open()legge e scrive (w+) i dati della pagina dal nostrorequests.get(data.text) e memorizza localmente i file HTML appena creati
Per evitare un problema crescente comune nello scraping del sito, memorizziamo queste pagine come file HTML locali. Vedi, quando visiti una pagina del sito, il server che ospita detta pagina onora la tua richiesta e invia i dati appropriati al tuo browser. Ma avere un client specifico che richiede ripetutamente le stesse informazioni mette a dura prova il server. L'amministratore del server ha tutti i diritti di bloccare queste richieste persistenti per poter fornire in modo ottimale questo servizio ad altri online.
Scaricando questi file HTML sul tuo computer locale, eviti due cose:
- Dover aspettare più del solito per raccogliere gli stessi dati
- Essere bloccati dalla visita della pagina, interrompendo del tutto la raccolta dei dati
Ci sono alcuni vantaggi nello scraping dei dati dai formati HTML. Uno dei più importanti è il modo in cui le proprietà degli elementi possono essere identificate in modo univoco. Se possono essere identificati in modo univoco, possono essere raschiati usando Beautiful Soup. Ma per farlo, bisogna sapere come ispezionare questi stessi elementi HTML.
Come ispezionare
Avremo bisogno di approfondire il funzionamento interno di questo documento, ma prometto che non ne farò un esercizio sull'apprendimento dell'HTML.
Se sai come ispezionare oggetti HTML, sentiti libero di fare un salto avanti. Altrimenti, segui come ispezionare gli elementi della pagina.
- Fai clic sui tre punti verticali nella barra dei menu in alto di Chrome
- Scegli "Altri strumenti"
- Seleziona Strumenti per sviluppatori.
- Fare clic con il tasto destro sulla pagina web
- Scegli "Ispeziona" per accedere al pannello degli strumenti per sviluppatori
Ispezione della pagina
Visto che tutte queste pagine sono memorizzate localmente, possiamo scegliere di visualizzarle entrando nel file system per aprirle nel nostro browser desiderato, oppure possiamo continuare a costruire il nostro codice implementando il seguente frammento di codice.
with open("notebooks/Rookies/2000.html") as f:
page = f.read()
Tutti i dati desiderati di questa pagina sono contenuti in quell'elemento della tabella. Prima di risucchiare frettolosamente tutti questi dati così come sono, ora è il momento migliore per considerare se vale la pena raccogliere tutto su questo tavolo.
Pre-pulizia
Pre-pulizia potrebbe non essere una parola frequente nel tuo vocabolario, ma per quelli di voi che si vedono raschiare dati su base regolare, dovrebbe esserlo. Se vuoi evitare la frustrazione di sprecare ore di progresso in un progetto di raccolta dati, è meglio separare prima la pula dal grano.
Ad esempio, prendi nota dei tre elementi riquadrati in rosso.
Una riga funge da intestazione della tabella "principale". Le altre due righe sono istanze duplicate degli stessi artefatti trovati in alto. Questo schema si ripete ogni 20 righe.
Un'ulteriore ispezione rivela che tutte queste righe hanno lo stesso trtag HTML (riga della tabella). Ciò che distingue ciascuno di questi elementi da tutti gli altri sono i nomi delle loro classi.
- Riga di intestazione principale
— Classe =over_header - Ripeti righe di intestazione
— Classe =over_header thead - Riga della categoria statistica
— Classe =thead - L'
classesoggetto array conterrà tutti i nomi di classe dell'elemento di riga della tabella unwarted.
#array to house list of dataframes
dfs = []
#unnecessary table rows to be removed
classes = ["over_header", "over_header thead", "thead"]
Il decomposemetodo serve a rimuovere tali elementi indesiderati in una pagina. Secondo la documentazione ufficiale di Beautiful Soup ...
decompose()
Tag.decompose()rimuove un tag dall'albero, quindi lo distrugge completamente e il suo contenuto.
Di seguito è riportato uno snippet di codice in cui siamo stati in grado di ottimizzare il decomposemetodo con l'aiuto dei forloop.
#for loop to iterate through the years
for year in years:
with open("notebooks/Rookies/{}.html".format(year)) as f:
page = f.read()
soup = BeautifulSoup(page, "html.parser")
#for loop cleans up unnecessary table headers from reappearing in rows
for i in classes:
for tr in soup.find_all("tr", {"class":i}):
tr.decompose()
- Il
withmetodo fornisce al nostro codice la struttura per lapagevariabile per leggere i file HTML memorizzati localmente quando viene richiamata - Una classe parser HTML viene inizializzata creando un'istanza della classe Beautiful Soup e passando sia l'
pageoggetto stringa chehtml.parser. - Il secondo
forciclo scorre i valoriclassesnell'array - Il terzo
forciclo utilizza il metodo di Beautiful Soupfind_allper identificare gli elementi che hanno siatrtag che nomi di classe corrispondenti a quelli inclasses tr.decomposeserve per omettere interamente dalla pagina ciascuno degli elementi della riga della tabella identificati
Estrazione dei dati
Possiamo finalmente iniziare a lavorare sulla parte del codice che effettivamente estrae i dati dalla tabella. Ricorda che la tabella con tutti i dati rilevanti ha l'ID univoco HTML rookies. Le seguenti aggiunte al nostro codice serviranno per analizzare i dati di questa tabella.
#the years we wish to parse for
years = list(range(1990, 2017))
#array to house list of dataframes
dfs = []
#unnecessary table headers to be removed
classes = ["over_header","over_header thead", "thead"]
for year in years:
#url = url_base.format(year)
#data = requests.get(url)
##page is saved as an html and placed in Rookies folder
#with open("notebooks/Rookies/{}.html".format(year), "w+") as f:
#f.write(data.text)
#with open("notebooks/Rookies/2000.html") as f:
with open("notebooks/Rookies/{}.html".format(year)) as f:
page = f.read()
soup = BeautifulSoup(page, "html.parser") #for loop cleans up unnecessary table headers from reappearing in rows
for i in classes:
for tr in soup.find_all("tr", {"class":i}):
tr.decompose()
### Scraping Block ###
#identifies, scrapes, and loads rookie tables into one dataframe
rookie_table = soup.find(id="rookies")
rookies = pd.read_html(str(rookie_table))[0]
rookies["Year"] = year
dfs.append(rookies)
#new variable turns list of dataframes into single dataframe
all_rookies = pd.concat(dfs)
rookie_tableistanzia il metodo di Beautiful Soupfindper identificare l'elemento HTML con IDrookies- Vedendo che Pandas può leggere le tabelle HTML, la tabella rookie viene caricata in Pandas utilizzando il
read_htmlmetodo, passandovirookie_tablecome stringa - L'aggiunta alla fine
[0]trasformarookiesda un elenco di frame di dati in un singolo frame di dati - Una colonna "Anno" è la fine del
rookiesframe di dati dfs.append(rookies)serve per ospitare tutte le tabelle di ogni anno da principiante nell'ordine in cui sono state iterate in un elenco di frame di dati- Il metodo Pandas
concatviene utilizzato per combinare l'elenco dei frame di dati in un unico frame di dati:all_rookies
Il nostro passaggio finale consiste nel prendere tutte queste informazioni utili e pulite e archiviarle in un formato CSV facilmente leggibile dall'uomo e dalla macchina. L'aggiunta di questa riga alla fine del nostro codice servirà per essere utile quando si decide di tornare indietro e fare riferimento ai dati raccolti.
#a single dataframe archived as a local CSV using Pandas
all_rookies.to_csv("archive/NBA_Rookies_1990-2016.csv")
import requests
import pandas as pd
from bs4 import BeautifulSoup
years = list(range(1990, 2017))
dfs = []
classes = ["over_header","over_header thead", "thead"] #unnecessary table headers to be removed
for year in years:
with open("C:/Users/cisco/OneDrive/python/Projects/Basketball/Rookies/{}.html".format(year), encoding = "utf-8") as f:
page = f.read()
soup = BeautifulSoup(page, "html.parser")
#second for loop cleans up unnecessary table headers from reappearing in rows
for i in classes:
for tr in soup.find_all("tr", {"class":i}):
tr.decompose()
#identifies, scrapes, and loads rookie tables into one dataframe
table_rookies = soup.find(id="rookies")
rookies = pd.read_html(str(table_rookies))[0]
rookies["Year"] = year
dfs.append(rookies)
#new variable turns list of dataframes into single dataframe
all_rookies = pd.concat(dfs)
#dataframe archived as local CSV
all_rookies.to_csv("archive/NBA_Rookies_1990-2016.csv")
Ancora una volta, il processo seguito in questa procedura dettagliata si applicherà senza dubbio a quasi tutte le altre pagine su Basketball-Reference.com . Ci sono cinque semplici passaggi che vale la pena seguire in ogni caso.
- Identifica l'URL della pagina
- Scarica la Pagina
- Identifica gli elementi
- Pre-pulire ed estrarre
- Archivio
Il prossimo capitolo di questa serie utilizzerà effettivamente questi dati per ottenere informazioni sul potenziale del futuro giocatore. Quindi stai attento alle puntate future!

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































