Python ile Süper Lig Puan Cetveli Çekme ve Sonuçları Tableau’da Görselleştirme

Kürşat Yalın
İstanbul Data Science Academy
7 min readSep 14, 2022

Merhabalar, İstanbul Data Science Academy’nin düzenlediği Data Engineering & Big Data BootCamp öğrencisi olarak ilk yazımı Python ile web kazıma konusunda yazıyorum. Yazının konusu Süper Lig’ten istenilen hafta kadar puan cetveli çekmek ve bunu Excel dosyasına yazdırmaktır. Daha sonra sonuçları Tableau’da görselleştireceğiz. Şimdi başlayalım.

Web kazıma yaparken Python’ın requests ve Beautiful Soup kütüphanelerini kullanacağız. İlk önce puan cetvelini çekeceğimiz siteye bir bakalım. Süper Lig puan cetveli için önce tff.org, daha sonra burada sağ orta kenarda Fikstür ve Puan Cetvelleri açılır-kapanır menüsünden Spor Toto Süper Lig’e tıklayınız.

Tıkladıktan sonra puan cetvellerinin bulunduğu sayfaya yönlendirileceksiniz.

Bu sayfada otomatik olarak mevcut haftanın puan cetveli çıkmaktadır. Yazıyı yazdığım sırada 6. haftada olduğumuz için, 6. haftanın puan cetvelini görmekteyiz. Şimdi kodumuzu yazmaya başlayalım. İlk olarak ilgili kütüphaneleri import edelim.

import requestsfrom bs4 import BeautifulSoupimport pandas as pd

Daha sonra siteye istek yollayalım ve dönen cevabı r değişkeninde tutalım. Burada timeout (Türkçe zaman aşımı) parametresini 15 yapalım. Bu parametre hem bağlanma (connect) hem de okuma (read) zaman aşımını kapsamakta olup saniye cinsindendir.

url = "https://www.tff.org/default.aspx?pageID=198"r = requests.get(url,timeout=15)

Aşağıdaki kod ile web sitesinden bize gelen cevabın, text property’si ile içeriğini göreceğiz.

html_doc = r.text

html_doc değişkenini yazdırdığımızda aşağıdaki url sayfamızın html yapısını görmekteyiz. Şimdi tüm sayfayı elde ettik. Bundan sonra istediğimiz bilgileri parse (ayrıştırmak) edebiliriz. Bunun için Beautiful Soup kütüphanesi kullanacağız. Bu kütüphaneyi en başta import etmiştik.

BeautifulSoup’taki html_parser ile yukarıdaki html dokümanımızı (html_doc) hiyerarşik bir html nesnesine dönüştürüp bunu soup değişkeninde tutuyoruz. Bundan sonra soup nesnesi içinde istediğimiz bilgileri çekebileceğiz.

soup = BeautifulSoup(html_doc, 'html_parser')

Ekrandaki puan cetveline sağ tıklayıp İncele’ye tıkladığımızda puan cetvelinin hangi html öğesinde (div öğesi) olduğunu görmekteyiz. Burada find metodunu kullanarak bu öğeyi bulalım. Tek bir öğeyi istediğimiz için find metodunu kullanacağız.

puan_cetveli_genel = soup.find('div', {'id':"ctl00_MPane_m_198_10561_ctnr_m_198_10561_Panel1"})

Artık puan cetvelini aldık. Puan cetvelindeki elemanları daha planlı bir şekilde alabilmek için aşağıdaki şekilde Takım İsimleri, Başlık ve Değerler şeklinde üç farklı bölüme ayırdım.

Önce başlık ve değerlerden başlayalım. Başlık ve değerler span öğesinin içinde bulunmaktadır. Bundan dolayı tüm span öğelerini bulmamız gerekir. Bunun için find_all metodunu kullanacağız.

baslik_ve_degerler = puan_cetveli_genel.find_all('span')

Başlık ve değerleri yukarıdaki gibi alıp baslik_ve_degerler isimli bir değişkende tutalım. baslik_ve_degerler değişkenimizi yazdırdığımız zaman puan cetvelindeki takım isimleri hariç başlık ve değerleri içeren span öğelerini bir liste şeklinde elde ettiğimizi görürsünüz. find_all metodu liste döndürmektedir.

Şimdi sırasıyla başlıkları, daha sonra ise değerleri ayrı bir liste içine atalım. Başlıklar O, G, B, M, A, Y, AV, P listemizin ilk 8 span öğesinin içindedir. (O: Oynanan, G: Galibiyet, B: Beraberlik, M: Mağlubiyet, A: Attığı gol, Y: Yediği gol, AV: Averaj, P: Puan) Bu öğelerdeki başlık bilgilerini almak için text property’sini kullanacağız. Aşağıdaki başlık bilgilerimizi baslik değişkeninde liste şeklinde tutuyoruz.

baslik = []for b in baslik_ve_degerler[0:8]:
baslik.append(b.text)
# Alternatif kod (list comprehension ile)
baslik = [b.text for b in baslik_ve_degerler[0:8]]

Şimdi de değerleri alalım. Değerler de başlıklarının bitiminde itibaren başlamaktadır. Burada dikkat ederseniz i.text’i integer’a çevirdik. Bunun nedeni i.text’in sonucunun metin tipinde olmasıdır. Fakat değerler, sayısal bilgi içerdiği için tam sayı tipi gerekmektedir.

degerler = []for i in baslik_ve_degerler[8:]:
degerler.append(int(i.text))

Bir sonraki aşama takım isimlerini oluşturmaktır. Takım isimleri puan_cetveli_genel değişkeninde a öğelerinin içindedir. find_all metodu ile tüm a öğelerini bulalım ve bunu daha sonra takim_listesi isminde bir liste tipi bir değişkende saklayalım.

takimlar = puan_tablosu_genel.find_all('a')takim_listesi = []for takim in takimlar:
takim_listesi.append(takim.text)

Artık puan cetvelindeki takım listesi, başlık ve değerlerin hepsini aldık ve bunları ayrı ayrı bir listede sakladık.

Burada bir sonraki yapacağımız işlem degerler listesindeki sayıları 8'li gruplar halinde ayrı bir listede tutmaktır (liste içinde liste). Bunun nedeni değerler listesinde 8'li sayı gruplarının her birinin bir takıma ait değerleri göstermesidir.

takım_degerler = [degerler[x:x+8] for x in range(0,len(degerler),8)]

Bundan sonra yapacağımız işlem her bir takımı ilgili 8'li grup takım değerleri ile eşleştirmektir. Bunun için eşleştirme yapan zip() fonksiyonunu kullanacağız ve bunu dict() fonksiyonu ile bir dictionary (sözlük) veri yapısına dönüştürüp puan_cetveli değişkeninde tutacağız.

puan_cetveli = dict(zip(takim_listesi, takım_degerler))

puan_cetveli değişkenini yazdırdığımızda aşağıdaki takım ve istatistiklerinin eşleştiği bir dictionary elde ettiğimizi görürüz.

Şu an elimizde puan cetveli ve başlık bilgileri var. Nihai olarak bunları bir dataframe’in içinde birleştireceğiz. Bundan önce ise bir işlem daha kalıyor. Şu anki veri kazıdığımız sayfa 6.haftanın bilgilerini içermektedir. Fakat bize diğer haftalara ait de istatistik gerekmektedir. Peki bu hafta bilgisini nereden alacağız?

Hafta bilgisi, puan cetvelinin hemen üstünde yer almaktadır. Buraya gelip sağ tıklatıp incele dediğimizde hangi html öğesinin (div) içinde olduğunu görmekteyiz. Şimdi bunu find ile çekelim ve ust_tablo isminde bir değişkende tutalım.

ust_tablo = soup.find('div',{'id':"ctl00_MPane_m_198_10561_ctnr_m_198_10561_ht"}

ust_tablo değişkenini yukarıdaki gibi yazdırdığımız zaman hafta bilgilerinin td öğesinde olduğunu görürüz ve class özniteliklerine (attribute) baktığımızda ise haftaNoOn ve haftaNoOff değerlerini aldığını görürüz. Güncel hafta her zaman haftaNoOff , diğer haftalar ise haftaNoOn değerini almaktadır. Şimdi güncel hafta bilgisini bulup bunu hafta değişkeninde saklayalım.

hafta = ust_tablo.find('td', {'class':'haftaNoOff'}).text

hafta değişkenini yazdırdığımızda güncel hafta bilgisini ‘6’ yı ekranda görmekteyiz.

Şimdi yukarıda elde ettiğimiz tüm bilgileri puan_cetveli_df isminde bir dataframe içinde toplayalım.

puan_cetveli_df = pd.DataFrame.from_dict(data=puan_cetveli, orient='index',columns=baslik)

Index sütunumuz takımları gösterdiği için bunu resetleyip Takım ismini verelim.

puan_cetveli_df = puan_cetveli_df.reset_index().rename(columns={'index':'Takım'})

Evet. Puan cetvelimiz yavaş yavaş şekillenmeye başladı. Şimdi takım isimlerinin önünde bulunan “1.”, “2.” … “18.” sıra numaralarını kaldıralım. Bunun için replace metodunu regex parametresi ile kullanacağız. Aşağıdaki kod parçacığı takımların önündeki sayı ve noktadan oluşan karakter gurubunu bulup boş karakter ile değiştirmektedir.

puan_cetveli_df['Takım'].replace(regex=r'^(\d+\.)', value='', inplace=True)

Şimdi puan cetvelimize Hafta ve Sıra sütunlarını ekleyelim. Bu şekilde hangi haftada olduğumuz ve takımın kaçıncı sırada olduğunu görebiliriz. Burada insert() metodunu kullanacağız. Insert metodunun kullanımı için buraya tıklayınız.

puan_cetveli_df.insert(1, "Hafta", hafta)puan_cetveli_df.insert(0, 'Sıra', range(1, len(puan_cetveli_df)+1))

Evet. Şu an puan cetvelini istediğimiz formatta elde ettik. Bu tabloyu excele yazdırmadan önce birkaç işlem daha yapmamız gerekmektedir. Şu anki tablo sadece mevcut haftayı çekmektedir. Fakat bize diğer haftalar da gerekmektedir. Bunun url adresini dinamikleştirmemiz gerekiyor. url adresine hafta parametresini ekleyip değerini 1 yaptığımızda 1. hafta puan cetveli sayfasına gittiğimizi görmekteyiz. Eğer hafta=2 yazsaydık 2. hafta puan cetveli sayfasına gidecektik.

Şimdi en başta yazdığımız url değişkenine hafta_sayisi parametresini ekleyelim.

url = 'https://www.tff.org/default.aspx?pageID=198&hafta='+str(hafta_sayisi)

Birazdan hafta_sayisi değişkenimize nasıl değer göndereceğimizi göstereceğim. Bundan önce diger_hafta ve GUNCEL_HAFTA değişkenlerini oluşturalım. Yukarıda hafta diye bir değişken tanımlamıştık. Bu hafta değişkeni güncel hafta bilgisini içermektedir. Ayrıca güncel haftanın her zaman class’ı haftaNoOff değerini, diğer haftaların class’ı da her zaman haftaNoActive değerini aldığını ifade etmiştik. Şimdi yukarıdaki hafta değişkenini tanımladığımız kodun yerine aşağıdaki kodu yazacağız. Aşağıdaki kodda önce güncel hafta (6) değerini alıp bunu tam sayıya dönüştürüyoruz. Daha sonra hafta sayisi güncel haftaya geldiği zaman GUNCEL_HAFTA değerini diğer_hafta değerine atarız. Burada hafta_sayisi değişkenini birazdan bir döngü içinde dinamik bir şekilde değiştireceğiz.

GUNCEL_HAFTA = int(ust_tablo.find('td', {'class':'haftaNoOff'}).text)if hafta_sayisi != GUNCEL_HAFTA:
diger_hafta = int(ust_tablo.find('td'
{'class':'haftaNoActive'}).text)
else:
diger_hafta = GUNCEL_HAFTA

Ama öncesinde dataframe’i Excel’e nasıl yazdırılacağına bakalım. İlk önce birinci haftayı superlig_puan_durumu isminde bir excel dosyasına yazdırırız. Diğer haftaları ise ExcelWrite() fonksiyonu ve a modunda superlig_puan_durumu dosyasına ekleriz. Buradaki a, append (Türkçe eklemek) kelimesinden gelmiştir.

if diger_hafta == 1:
puan_cetveli_df.to_excel('superlig_puan_durumu.xlsx',
index=False, sheet_name=str(hafta_sayisi)+".hafta")
else:
with pd.ExcelWriter('superlig_puan_durumu.xlsx',mode='a')
as writer:
puan_cetveli_df.to_excel(writer, index=False,
sheet_name=str(hafta_sayisi)+".hafta")

Şimdi tüm yukarıda yazdığımız kodları bir fonksiyon içinde toplayalım.

Yukarıdaki fonksiyonda dikkat ederseniz GUNCEL_HAFTA değişkenini fonksiyon içinde global bir değişken olarak tanımladık ve fonksiyon dışında 1 değeri ile başlattık. Aşağıdaki fonksiyon ile de puan cetvelimizi oluşturuyoruz. puan_cetveli_yazdir (hafta_sayisi) fonksiyonu, hafta_sayisi isminde bir argüman almaktadır. Bu argümana değerleri ise aşağıdaki fonksiyonda while döngüsünde 1 arttırarak vermekteyiz. GUNCEL_HAFTA global değişkeni ise ilk fonksiyonumuzda güncel hafta değerini almaktadır. Bu değer 6.hafta için 6'dır. Yani bu şekilde aşağıdaki fonksiyon ile 1'den güncel haftaya kadar tüm haftaları yazdırmış oluyoruz.

Şimdi Excel dosyamıza bakalım. Evet aşağıda da gördüğümüz gibi 6 haftayı da aynı çalışma kitabına ve farklı sayfalara yazdırdık.

Son olarak bu sonuçları Tableau’da görselleştirelim. Burada takımların haftalık bazında sıralamasını görselleştirdim. 6.hafta maçları tamamlanmadığından sadece ilk 5 haftanın sıralamasını kullandım.

Görselleştirmenin etkileşimli halini görmek için buraya tıklayınız.

Kodun tamamı aşağıdadır.

Umarım faydalı bir yazı olmuştur.

--

--