HTML'den Veri Ayıklamak: BeautifulSoup ile Web Kazıma
Bu derste ne yapacağız?
Bu derste bir web sayfasının HTML kodunun içinden başlık, fiyat veya bağlantı gibi belirli verileri seçip ayıklamayı öğreneceğiz. Bunun için Python'ın en yaygın web kazıma kütüphanelerinden biri olan BeautifulSoup'u kullanacağız. Dersin sonunda, bir sayfadan çektiğimiz verileri bir önceki derste openpyxl ile öğrendiğimiz yöntemle otomatik olarak bir Excel dosyasına kaydedeceğiz; böylece iki dersi birleştirip uçtan uca çalışan küçük bir araç ortaya çıkaracağız.
Ön koşul olarak şunları bilmeniz beklenir: temel Python söz dizimi (değişkenler, döngüler, listeler, fonksiyonlar), dosyalarla çalışma mantığı ve bir önceki derste işlediğimiz openpyxl ile Excel'e veri yazma. Ayrıca bilgisayarınızda Python'ın kurulu olması ve bir komut satırından (terminal veya PowerShell) pip komutunu çalıştırabiliyor olmanız gerekir. HTML hakkında hiçbir bilgi gerekmiyor; sayfanın yapısını birlikte inceleyeceğiz.
Kavram
Bir web sayfası, tarayıcınızın yorumladığı HTML adlı bir metin biçimindedir. HTML, sayfadaki her öğeyi (başlık, paragraf, resim, tablo, bağlantı) etiketlerle işaretler; örneğin bir başlık <h1>Merhaba</h1> şeklinde, bir bağlantı ise <a href="...">metin</a> şeklinde yazılır. Web kazıma (web scraping), bir sayfanın HTML kodunu indirip bu etiketlerin içindeki bilgileri programatik olarak ayıklama işlemidir.
Bunu bir markete benzetebilirsiniz: Market rafları (HTML etiketleri) ürünleri (verileri) belirli bir düzende tutar. Siz manuel olarak her rafı tek tek dolaşıp ürün adını ve fiyatını deftere yazmak yerine, bir yardımcıya (BeautifulSoup) "bana sadece kırmızı etiketli rafların üzerindeki fiyatları getir" dersiniz. Yardımcı, rafların düzenini anlar ve istediğiniz bilgiyi süzüp size verir. BeautifulSoup da tam olarak bunu yapar: HTML metnini analiz eder, onu bir ağaç yapısına (etiketlerin iç içe geçmiş hâli) dönüştürür ve siz belirli etiket adı, sınıf (class) ya da kimlik (id) vererek istediğiniz veriyi seçebilirsiniz.
Web kazımanın iki temel adımı vardır: birincisi sayfanın HTML içeriğini indirmek (bunun için requests kütüphanesini kullanırız), ikincisi bu HTML içeriğini ayrıştırıp içinden istediğimiz parçaları çekmek (bunun için BeautifulSoup kullanırız). Bu iki kütüphane genellikle birlikte kullanılır ve web kazımanın standart ikilisidir.
Önemli bir uyarı: Her sitenin kazınmasına izin verilmez. Bir siteyi kazımadan önce sitenin kullanım şartlarını ve robots.txt dosyasını kontrol etmek, aşırı sıklıkta istek göndererek sunucuyu yormamak ve yalnızca herkese açık, kazımaya izin veren veya kendi kontrolünüzde olan sayfalarla pratik yapmak önemlidir. Bu derste örnek olarak basit, statik ve eğitim amaçlı yapılmış bir HTML sayfası üzerinde çalışacağız.
Adım adım uygulama
- Gerekli kütüphaneleri kurun. Terminal veya PowerShell'i açın ve şu komutu çalıştırın:
Bu komut üç kütüphaneyi birden kurar: sayfa indirmek içinpip install requests beautifulsoup4 openpyxlrequests, HTML'i ayrıştırmak içinbeautifulsoup4ve bir önceki dersten hatırlayacağınız Excel dosyası oluşturmak içinopenpyxl. - Örnek bir HTML sayfası oluşturun. Gerçek bir siteyle uğraşmadan önce mantığı öğrenmek için kendi bilgisayarınızda basit bir HTML dosyası hazırlayalım. Bir metin düzenleyicide (Not Defteri, VS Code gibi) yeni bir dosya açın, adını
urunler.htmlkoyun ve şu içeriği yazıp kaydedin:
Bu sayfada üç adet ürün kutusu (<html> <body> <div class="urun"> <h2>Kablosuz Kulaklık</h2> <span class="fiyat">450 TL</span> <a href="urun1.html">Detay</a> </div> <div class="urun"> <h2>Bluetooth Hoparlör</h2> <span class="fiyat">620 TL</span> <a href="urun2.html">Detay</a> </div> <div class="urun"> <h2>Akıllı Saat</h2> <span class="fiyat">1350 TL</span> <a href="urun3.html">Detay</a> </div> </body> </html>div class="urun") var; her kutunun içinde bir ürün adı (h2), bir fiyat (span class="fiyat") ve bir bağlantı (a) bulunuyor. - HTML dosyasını Python'da açıp BeautifulSoup ile ayrıştırın. Aynı klasörde
kazima.pyadında yeni bir Python dosyası oluşturun ve şunu yazın:
Satır satır ne yaptığını inceleyelim:from bs4 import BeautifulSoup with open("urunler.html", "r", encoding="utf-8") as dosya: icerik = dosya.read() soup = BeautifulSoup(icerik, "html.parser") print(soup.prettify())from bs4 import BeautifulSoupsatırı kütüphaneyi projemize dahil eder.with open(...)bloğu HTML dosyasını okur ve içeriğiniicerikdeğişkenine metin olarak alır.BeautifulSoup(icerik, "html.parser")satırı bu ham metni analiz edilebilir bir ağaç yapısına dönüştürür;"html.parser"Python'ın kendi dahili HTML ayrıştırıcısıdır ve ek kurulum gerektirmez.soup.prettify()ise HTML'i girintili, okunaklı bir biçimde ekrana yazdırır; bu, yapıyı görsel olarak anlamak için kullanışlıdır. - Belirli etiketleri seçin. Şimdi tüm ürün kutularını bulup içlerindeki bilgileri çekelim.
kazima.pydosyasını şu şekilde güncelleyin:urun_kutulari = soup.find_all("div", class_="urun") for kutu in urun_kutulari: ad = kutu.find("h2").text fiyat = kutu.find("span", class_="fiyat").text link = kutu.find("a")["href"] print(ad, "-", fiyat, "-", link)soup.find_all("div", class_="urun")sayfadakiclassdeğeri"urun"olan tümdivetiketlerini bir liste olarak döndürür (Python'daclassanahtar kelime olduğu için BeautifulSoup'taclass_yazılır, sonuna alt çizgi eklenir).for kutu in urun_kutulari:döngüsü her ürün kutusunu tek tek gezer.kutu.find("h2").texto kutunun içindeki ilkh2etiketini bulur ve yalnızca yazı içeriğini (.text) alır.kutu.find("span", class_="fiyat")yine kutunun içinde, sınıfı"fiyat"olanspanetiketini bulur.kutu.find("a")["href"]ise kutudaki bağlantı etiketini buluphrefözniteliğinin değerini (yani bağlantının adresini) alır; burada köşeli parantez kullanmamızın sebebi, bir etiketin bir özniteliğine sözlük gibi erişebilmemizdir. - Gerçek bir web sayfasından veri çekmeyi deneyin. Kendi kontrolünüzde olan veya kazımaya açık, basit bir test sayfası varsa
requestsile indirebilirsiniz:import requests from bs4 import BeautifulSoup adres = "https://ornek-site.com/urunler" cevap = requests.get(adres, timeout=10) cevap.raise_for_status() soup = BeautifulSoup(cevap.text, "html.parser")requests.get(adres, timeout=10)belirtilen adrese bir istek gönderir ve sunucudan gelen yanıtı bekler;timeout=10sunucu 10 saniye içinde cevap vermezse programın sonsuza kadar beklemesini önler.cevap.raise_for_status()sunucudan hata kodu (örneğin sayfa bulunamadı) geldiyse programı anlamlı bir hatayla durdurur.cevap.textsunucudan gelen HTML içeriğini metin olarak verir ve bunu doğrudan BeautifulSoup'a besleriz. Buradan sonrası bir önceki adımdakifind_allvefindmantığıyla aynıdır; yalnızca gerçek sitenin etiket adları ve sınıf isimleri farklı olacağı için tarayıcınızın "Öğeyi denetle" özelliğiyle önce o sayfanın yapısını incelemeniz gerekir. - Çekilen verileri bir Excel dosyasına kaydedin. Şimdi bir önceki derste öğrendiğimiz openpyxl'i devreye sokalım. Örnek HTML dosyamızdan çektiğimiz verileri kaydeden tam kodu yazalım:
Bu kodda yeni olan kısımları inceleyelim:from bs4 import BeautifulSoup from openpyxl import Workbook with open("urunler.html", "r", encoding="utf-8") as dosya: icerik = dosya.read() soup = BeautifulSoup(icerik, "html.parser") urun_kutulari = soup.find_all("div", class_="urun") kitap = Workbook() sayfa = kitap.active sayfa.title = "Urunler" sayfa.append(["Ürün Adı", "Fiyat", "Bağlantı"]) for kutu in urun_kutulari: ad = kutu.find("h2").text fiyat = kutu.find("span", class_="fiyat").text link = kutu.find("a")["href"] sayfa.append([ad, fiyat, link]) kitap.save("urunler_listesi.xlsx") print("Excel dosyası oluşturuldu: urunler_listesi.xlsx")Workbook()boş bir Excel çalışma kitabı oluşturur.kitap.activeo kitaptaki aktif çalışma sayfasını seçer.sayfa.append([...])satırı, verdiğimiz listeyi Excel'de yeni bir satır olarak ekler; önce başlık satırını ("Ürün Adı","Fiyat","Bağlantı") ekliyoruz, sonra döngü içinde her ürün için bir satır daha ekliyoruz.kitap.save("urunler_listesi.xlsx")son olarak dosyayı diske kaydeder. Bu kodu çalıştırdığınızda, çalıştığınız klasördeurunler_listesi.xlsxadında, üç ürünün adı, fiyatı ve bağlantısını içeren bir Excel dosyası oluşacaktır. - Sonucu kontrol edin. Oluşan
urunler_listesi.xlsxdosyasını Excel veya benzeri bir programla açın ve verilerin doğru satır ve sütunlara yerleştiğinden emin olun. Eğer bir hücre boş veya yanlış görünüyorsa, ilgili HTML etiketinin adını veya sınıfını tekrar kontrol edin.
Sık karşılaşılan sorunlar
| Hata / Sorun | Olası Sebep ve Çözüm |
|---|---|
ModuleNotFoundError: No module named 'bs4' | BeautifulSoup kurulu değil. Terminalde pip install beautifulsoup4 komutunu çalıştırın; sanal ortam kullanıyorsanız ortamın etkin olduğundan emin olun. |
AttributeError: 'NoneType' object has no attribute 'text' | find aradığınız etiketi bulamadı ve None döndürdü. Etiket adının veya sınıf isminin doğru yazıldığını, sayfanın yapısının beklediğiniz gibi olduğunu kontrol edin; tarayıcının "Öğeyi denetle" aracıyla gerçek HTML'e tekrar bakın. |
requests.exceptions.ConnectionError veya zaman aşımı | İnternet bağlantısı yok, adres yanlış yazılmış veya sunucu erişime kapalı. Adresi tarayıcıda açarak test edin, timeout süresini artırmayı deneyin. |
| Excel dosyası oluşuyor ama tüm hücreler boş | Döngü içinde sayfa.append çağrısı yanlış konumda veya listeler boş geliyor olabilir. find_all sonucunun boş liste dönüp dönmediğini print(len(urun_kutulari)) ile kontrol edin. |
| Sitenin HTML yapısı beklenenden çok farklı, veriler karışık geliyor | Modern siteler JavaScript ile içerik oluşturabilir; bu durumda requests ile indirilen HTML'de veri hiç bulunmayabilir. Bu, bu dersin kapsamı dışındadır ve tarayıcı otomasyonu gerektiren farklı bir konudur. |
| Kazıma sırasında "403 Forbidden" hatası alınıyor | Site otomatik isteklere izin vermiyor olabilir. Bu, sitenin kullanım şartlarına saygı gösterilmesi gereken bir sinyaldir; o siteyi kazımaktan vazgeçip yalnızca izin verilen kaynaklarla çalışın. |
Mini görev
Bu derste kullandığımız urunler.html dosyasına iki ürün daha ekleyin (aynı div class="urun" yapısını kullanarak). Ardından Python kodunu, bu beş ürünün tamamını okuyup fiyatı 600 TL'nin üzerinde olanları ayrı bir Excel dosyasına (pahali_urunler.xlsx) kaydedecek şekilde güncelleyin. İpucu: fiyat metnindeki " TL" ifadesini kaldırıp geri kalan kısmı sayıya çevirmeniz (int() veya benzeri) gerekecek.
Kontrol listesi:
- HTML dosyasında toplam beş ürün kutusu var mı?
- Kod, fiyat metnindeki "TL" ifadesini temizleyip sayısal karşılaştırma yapabiliyor mu?
- Sadece 600 TL üzeri ürünler
pahali_urunler.xlsxdosyasına yazılıyor mu? - Excel dosyasında başlık satırı (Ürün Adı, Fiyat, Bağlantı) doğru şekilde yer alıyor mu?
- Kodu ikinci kez çalıştırdığınızda hata almadan yeniden çalışıyor mu?
Kendini sına
1. BeautifulSoup'un temel görevi nedir?
2. soup.find_all("div", class_="urun") satırı ne döndürür?
3. Bir etiketin metin içeriğini almak için hangi öznitelik kullanılır?
4. Bir bağlantı etiketinin (<a>) hedef adresine nasıl erişilir?
5. Bir siteyi kazımadan önce kontrol edilmesi gereken, sitenin izinlerini belirten dosyanın adı nedir?
Cevaplar
1. HTML metnini analiz edilebilir bir ağaç yapısına dönüştürüp, etiket adı, sınıf veya kimlik gibi kriterlerle istenen verilerin seçilmesini sağlamaktır.
2. Belirtilen etiket adına ve sınıfına (class_="urun") sahip tüm eşleşen etiketlerin bir listesini döndürür.
.text özniteliği kullanılır.
4. Etiketin href özniteliğine köşeli parantez ile erişilir: etiket["href"].
5. robots.txt dosyasıdır.
Bir önceki derste openpyxl ile Excel dosyalarını sıfırdan oluşturmayı, veri yazmayı ve biçimlendirmeyi öğrenmiştik; bu derste ise web sayfalarından veri çekip o bilgiyi doğrudan Excel'e aktararak iki beceriyi birleştirdik. Kursun son dersinde, şimdiye kadar öğrendiğimiz dosya işleme, Excel otomasyonu ve web kazıma tekniklerini bir araya getirerek uçtan uca çalışan küçük bir otomasyon aracı geliştireceğiz.
Bu dersi kayıt olmadan izleyebilirsin. İlerlemeni kaydetmek, sertifika almak ve puan tablosuna girmek için ücretsiz üye ol: Kayıt Ol