Python Kullanarak Basketbolun Maskesini Kaldırmak

Jan 24 2023
Bölüm 1: Güzel Çorba ile Kazıma
Bu blog dizisinde, basketboldaki bazı gizli istatistiksel gerçekleri ortaya çıkarmaya yardımcı olmak için güçlü Python kitaplıklarından yararlanılıyor. Herhangi bir veriye dayalı yaklaşımda ilk adım, ihtiyaç duyulan verileri belirlemek ve toplamaktır.

Bu blog dizisinde, basketboldaki bazı gizli istatistiksel gerçekleri ortaya çıkarmaya yardımcı olmak için güçlü Python kitaplıklarından yararlanılıyor. Herhangi bir veriye dayalı yaklaşımda ilk adım, ihtiyaç duyulan verileri belirlemek ve toplamaktır.

Şansımıza, Basketball-Reference.com basketbol verilerinin kolayca kopyalanabilecek sayfalarını barındırıyor. Bu incelemenin süreçleri, herhangi bir sayıda sayfaya kolayca uygulanabilir, ancak bu durumda, birden çok çaylak sınıfının sezonluk istatistiklerini kazımayı planlıyoruz.

Görevler

  1. Veri Kaynağını Tanımlayın
  2. Sayfayı İndir
  3. Önemli Sayfa Öğelerini Belirleyin
  4. Ön Temizleme ve Çıkarma
  5. Arşiv
  • İstek paketi
  • Güzel Çorba paketi
  • pandalar paketi

Veri Kaynağını Belirleme

Basketball-Reference.com, sayılar, ribaundlar ve şut yüzdeleri gibi tipik kutu skoru kategorilerinin sezonluk ortalamalarından son maçta oynanan her maçın oyun bazında aksiyonuna kadar değişen basketbol istatistikleriyle ilgili derlenmiş yüzlerce sayfaya ev sahipliği yapıyor. 20 yıl kadar. Tam olarak ne aranacağına dair net bir hedef belirlenmemişse, bu istatistiksel tsunamide kişi kolayca yolunu kaybedebilir.

Bu gönderideki amaç basit: genç bir oyuncunun gerçek değerini ve potansiyelini değerlendirmeye yardımcı olacak çaylak verilerini elde etmek.

Aşağıdaki bağlantı böyle bir sayfadır. Belirli bir sezondaki çaylakların ilgili tüm istatistiklerini listeler.

https://www.basketball-reference.com/leagues/NBA_1990_rookies-season-stats.html

Oyuncular hakkında sağlam istatistiksel çıkarımlar yapmaya yetecek kadar veri toplamak için, bir yıllık veri yeterli olmayacaktır. Gürültüyü filtrelemeye ve bir oyuncunun gelecekteki potansiyeli hakkında bir sonuca varmaya yardımcı olmak için onlarca yıllık verilerin toplanması gerekir.

Bir eylem manuel olarak tekrarlanabiliyorsa, kendisini otomasyon için harika bir aday yapar. Bu durumda, yukarıdaki URL'deki sayı, o çaylak sınıfının ilgili yılına karşılık gelir. Bu bilgiden güç alarak, ilk kod satırlarımızı oluşturmaya başlayalım.

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"

  1. Uygun paketler içe aktarılır
  2. Liste yearsdeğişkeni, istenen yılların aralığını belirtir - 1990'dan 2017'ye kadar
  3. url_basehedef URL'nin önceden biçimlendirilmiş dize değişkenini depolamaya yarar

Web sayfalarını sıyırırken, mümkün olduğu kadar fazla yükü kaldırmak zorunludur. Site, tüm bilgilerini HTML ön ucunda sakladığından, sayfa kolayca indirilebilir ve tamamı yerel olarak saklanabilir.

#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)

  1. ' dizisinde bulunan kaşlı ayraçlar, ' nin onu o anda yinelenen yılla değiştirmesine urlizin verecek - Örneğin, ilk yinelemesinde, değer şu olacaktır:format
    urlhttps://www.basketball-reference.com/leagues/NBA_1990_rookies-season-stats.html
    — İkinci yinelemesinde, bunun yerine sonraki yıl referans alınır:https://www.basketball-reference.com/leagues/NBA_1991_rookies-season-stats.html
  2. Veri değişkeni, şu anda yinelenen dize değerinin requests.get()işlevi ve referansları için bir yer tutucu görevi görür.url
  3. Yöntem requestsdaha sonra söz konusu sayfayı almak için yeni biçimlendirilmiş URL dizesini kullanır.
  4. Sonraki sayfa verilerini bizim ( ) sayfamızdan with open()okur ve yazar ( ) ve yeni oluşturulan HTML dosyalarını yerel olarak depolarw+requests.getdata.text

Site kazıma işleminde sık görülen bir sorundan kaçınmak için bu sayfaları yerel HTML dosyaları olarak saklarız. Bir sayfa sitesini ziyaret ettiğinizde, söz konusu sayfayı barındıran sunucu isteğinizi yerine getirir ve uygun verileri tarayıcınıza geri gönderir. Ancak belirli bir müşterinin aynı bilgileri tekrar tekrar istemesi, sunucu üzerinde gereksiz bir yük oluşturur. Sunucu yöneticisi, bu hizmeti çevrimiçi olarak başkalarına en iyi şekilde sunabilmek adına bu kalıcı istekleri engelleme hakları dahilindedir.

Bu HTML dosyalarını yerel makinenize indirerek iki şeyden kaçınırsınız:

  1. Aynı verileri toplamak için normalden daha uzun süre beklemek zorunda kalmak
  2. Sayfayı ziyaret etmesinin engellenmesi, veri toplamanın tamamen durdurulması

HTML biçimlerinden veri kazımanın birkaç avantajı vardır. En önemlilerinden biri, öğe özelliklerinin nasıl benzersiz bir şekilde tanımlanabileceğidir. Benzersiz bir şekilde tanımlanabiliyorlarsa, Güzel Çorba kullanılarak kazınabilirler. Ancak bunu yapmak için, bu HTML öğelerinin kendilerinin nasıl denetleneceğini bilmek gerekir.

Nasıl İncelenir

Bu belgenin iç işleyişine daha derinlemesine dalmamız gerekecek, ancak söz veriyorum, bunu HTML öğrenme alıştırması haline getirmeyeceğim.

HTML nesnelerini nasıl inceleyeceğinizi biliyorsanız, ileri atlamaktan çekinmeyin. Aksi takdirde, lütfen sayfa öğelerinin nasıl inceleneceğini takip edin.

  1. Chrome'un üst menü çubuğundaki üç dikey noktayı tıklayın
  2. "Diğer araçlar"ı seçin
  3. Geliştirici araçlarını seçin.
  1. Web sayfasına sağ tıklayın
  2. Geliştirici araçları paneline erişmek için "İncele"yi seçin

Sayfayı incelemek

Tüm bu sayfaların yerel olarak depolandığını görerek, onları istediğimiz tarayıcıda açmak için dosya sistemine girerek görüntülemeyi seçebilir veya aşağıdaki kod parçasını uygulayarak kodumuzu oluşturmaya devam edebiliriz.

with open("notebooks/Rookies/2000.html") as f:
    page = f.read()

Bu sayfanın istenen tüm verileri bu tablo öğesinde yer alır. Aceleyle tüm bu verileri olduğu gibi emmeden önce, şimdi bu tablodaki her şeyin toplanmaya değer olup olmadığını düşünmenin en iyi zamanı.

Ön Temizlik

Ön temizleme, kelime dağarcığınızda sık kullanılan bir kelime olmayabilir, ancak kendinizi düzenli olarak veri kazırken görenleriniz için öyle olmalıdır . Bir veri toplama projesinde saatlerce boşa harcanan ilerlemenin hayal kırıklığını önlemek istiyorsanız, önce samanı buğdaydan ayırmak en iyisidir.

Örneğin, kırmızıyla kutulanmış üç öğeye dikkat edin.

Bir satır, "ana" tablo başlığı olarak işlev görür. Diğer iki sıra, üstte bulunan aynı yapıtların yinelenen örnekleridir. Bu model her 20. sırada bir tekrarlanır.

Daha ayrıntılı inceleme, tüm bu satırların aynı tr(tablo satırı) HTML etiketine sahip olduğunu ortaya çıkarır. Bu öğelerin her birini diğerlerinden ayıran, sınıf adlarıdır.

  1. Ana Başlık Satırı
    — Sınıf =over_header
  2. Başlık Satırlarını Tekrarla
    — Sınıf =over_header thead
  3. İstatistik Kategori Satırı
    — Sınıf =thead
  4. #array to house list of dataframes
    dfs = []
    #unnecessary table rows to be removed
    classes = ["over_header", "over_header thead", "thead"]
    

  5. Dizi classesnesnesi, uyarılmamış tablo satırı öğesinin tüm sınıf adlarını tutacaktır.

Yöntem decompose, bir sayfadaki bu tür istenmeyen öğeleri kaldırmaya yarar. Resmi Güzel Çorba belgelerine göre ...

decompose()

Tag.decompose()ağaçtan bir etiketi kaldırır, ardından etiketi ve içeriğini tamamen yok eder.

decomposeAşağıda , yöntemi döngüler yardımıyla optimize edebildiğimiz bir kod parçacığı bulunmaktadır for.

#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()

  1. Yöntem with, kodumuza pagedeğişkenin çağrıldığında yerel olarak depolanan HTML dosyalarını okuması için yapı sağlar.
  2. Bir HTML ayrıştırıcı sınıfı, Beautiful Soup sınıfını başlatarak ve hem pagestring nesnesini hem de html.parser.
  3. İkinci döngü, dizideki fordeğerler arasında yinelenirclasses
  4. Üçüncü döngü, içindekilerle eşleşen hem etiketlere hem de sınıf adlarına sahip öğeleri tanımlamak için forGüzel Çorba'nın yöntemini kullanır.find_all trclasses
  5. tr.decomposetanımlanan tablo satırı öğelerinin her birini sayfadan tamamen çıkarmaya yarar

Verileri Çıkarma

Sonunda kodun tablodan veri çıkaran kısmı üzerinde çalışmaya başlayabiliriz. Tüm ilgili verilerin bulunduğu tablonun benzersiz HTML kimliğine sahip olduğunu unutmayın rookies. Aşağıdaki eklemeler, bu tablonun verilerini ayrıştırmak için kodumuza hizmet edecektir.

#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)

  1. rookie_tablefindKimliğe sahip HTML öğesini tanımlamak için Güzel Çorba'nın yöntemini başlatırrookies
  2. Pandaların HTML tablolarını okuyabildiğini gören çaylak tablo, yöntem kullanılarak Pandalara yüklenir read_htmlve içine rookie_tablestring olarak geçer.
  3. Sona doğru ilerleme, bir veri çerçeveleri listesinden tek bir veri çerçevesine [0]dönüşürrookies
  4. rookies"Yıl" sütunu, veri çerçevesinin sonudur
  5. dfs.append(rookies)her çaylak yılının tüm tablolarını veri çerçeveleri listesine yinelendikleri sırayla barındırmaya hizmet eder
  6. Pandalar yöntemi, concatveri çerçeveleri listesini tek bir veri çerçevesinde birleştirmek için kullanılır:all_rookies

Son adımımız, tüm bu faydalı, temiz bilgileri alıp insan ve makine tarafından kolayca okunabilen CSV formatında arşivlemeyi içeriyor. Bu satırı kodumuzun sonuna kadar yapıştırmak, geri dönüp toplanan verilere başvurmaya karar verirken faydalı olacaktır.

#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")

Yine, bu izlenecek yolda izlenen süreç hiç şüphesiz Basketball-Reference.com'daki diğer tüm sayfalar için geçerli olacaktır . Her durumda atmaya değer beş basit adım vardır.

  1. Sayfa URL'sini tanımlayın
  2. Sayfayı İndir
  3. Öğeleri Tanımlayın
  4. Ön Temizleme ve Çıkarma
  5. Arşiv

Bu dizide bir sonraki adım, gelecekteki oyuncu potansiyeli hakkında fikir edinmek için bu verileri gerçekten kullanacak. Bu yüzden gelecekteki taksitler için uyanık olun!