Python 3 ile Requests ve Beautiful Soup Kullanarak Web Verileriyle Çalışma
Giriş
İnternet, günümüzün en büyük bilgi deposudur. Haber sitelerinden e-ticaret platformlarına, akademik veri tabanlarından sosyal medya akışlarına kadar her yerde devasa miktarda veri bulunmaktadır. Bu verilerin manuel olarak toplanması ve analiz edilmesi çoğu zaman imkansızdır. İşte bu noktada “web scraping” (web kazıma) devreye girer. Web scraping, web sitelerinden otomatik olarak veri çekme işlemidir. Bu veriler daha sonra analiz, raporlama, otomasyon veya başka bir uygulama için kullanılabilir.
Python, web scraping için en popüler dillerden biridir ve bunu sağlayan güçlü kütüphanelere sahiptir. Bu makalede, Python 3’ün iki temel ve en sık kullanılan kütüphanesi olan Requests ve Beautiful Soup kullanarak web verileriyle nasıl çalışılacağını detaylı bir şekilde inceleyeceğiz. Requests, web sitelerine HTTP istekleri göndermemizi ve yanıtları almamızı sağlarken, Beautiful Soup ise bu yanıtlardan (genellikle HTML veya XML) istediğimiz verileri kolayca ayrıştırmamızı ve çıkarmamızı sağlar. Bu makalenin sonunda, temel web scraping projelerini kendi başınıza geliştirebilecek bilgi ve beceriye sahip olacaksınız.
Web Scraping Temelleri
Web scraping’e başlamadan önce, web’in nasıl çalıştığını ve web sayfalarının temel yapısını anlamak önemlidir.
HTTP ve Web Sayfalarının Yapısı
Web, istemci-sunucu mimarisi üzerinde çalışır. Tarayıcınız (istemci) bir web sitesine erişmek istediğinde, sunucuya bir HTTP (Hypertext Transfer Protocol) isteği gönderir. Sunucu bu isteği işler ve bir HTTP yanıtı döndürür. Bu yanıt genellikle HTML (Hypertext Markup Language), CSS (Cascading Style Sheets) ve JavaScript dosyalarını içerir.
* HTML: Bir web sayfasının iskeletini ve içeriğini tanımlar. Etiketler (tags) kullanarak metin, resim, bağlantı, tablo gibi öğeleri yapılandırır.
* CSS: Sayfanın görünümünü (renkler, fontlar, düzen) belirler.
* JavaScript: Sayfaya dinamik etkileşim ve işlevsellik katar.
Web scraping’de ana hedefimiz genellikle HTML içeriğidir, çünkü veriler bu yapının içine gömülüdür.
Etik ve Yasal Hususlar
Web scraping yaparken etik ve yasal sınırlamalara dikkat etmek hayati önem taşır:
1. robots.txt Dosyası: Bir web sitesinin kök dizininde bulunan robots.txt dosyası, arama motoru botlarının (ve genellikle diğer botların da) sitenin hangi bölümlerini tarayabileceğini veya tarayamayacağını belirtir. Bu dosyayı kontrol etmek ve belirtilen kurallara uymak iyi bir uygulamadır.
2. Kullanım Koşulları (Terms of Service): Birçok web sitesi, kullanım koşullarında otomatik veri toplama veya scraping’i yasakladığını belirtir. Bu koşulları ihlal etmek yasal sorunlara yol açabilir.
3. Veri Gizliliği: Kişisel verileri (ad, e-posta, telefon numarası vb.) toplarken GDPR (Genel Veri Koruma Yönetmeliği) veya KVKK (Kişisel Verilerin Korunması Kanunu) gibi veri gizliliği yasalarına uymak zorunludur.
4. Sunucu Yükü: Çok hızlı ve yoğun istekler göndermek, hedef sunucuya aşırı yük bindirebilir ve hizmet dışı kalmasına neden olabilir. Bu, sitenin yöneticileri tarafından IP adresinizin engellenmesine veya yasal işlem başlatılmasına yol açabilir. İstekler arasında gecikmeler (time.sleep()) eklemek önemlidir.
Bu makaledeki örnekler eğitim amaçlıdır. Gerçek dünyada scraping yaparken her zaman yukarıdaki hususları göz önünde bulundurunuz.
Requests Kütüphanesi ile HTTP İstekleri Yapma
Requests kütüphanesi, Python’da HTTP istekleri yapmak için tasarlanmış basit ve kullanıcı dostu bir kütüphanedir. Dahili urllib modülüne göre çok daha kolay bir API sunar.
Giriş
Requests, web sayfalarından veri almak, API’lerle etkileşim kurmak, form verileri göndermek ve daha fazlası için idealdir. Kurulumu ve kullanımı son derece basittir.
Kurulum
Requests kütüphanesini Python ortamınıza kurmak için pip kullanabilirsiniz:
pip install requests
GET İstekleri
Bir web sayfasının içeriğini almak için en temel istek türü GET isteğidir.
import requests
Bir web sitesine GET isteği yapma
url = "https://www.example.com"
response = requests.get(url)
Yanıtın durum kodunu kontrol etme
200 OK anlamına gelir, 4xx istemci hatası, 5xx sunucu hatası
if response.status_code == 200:
print("İstek başarılı!")
# Yanıtın metin içeriğini yazdırma
# print(response.text)
else:
print(f"İstek başarısız oldu. Durum kodu: {response.status_code}")
response nesnesi, HTTP isteğinin sonucu hakkında birçok bilgi içerir:
* response.status_code: HTTP durum kodu (örn. 200, 404, 500).
* response.text: Yanıtın metin içeriği (genellikle HTML).
* response.content: Yanıtın bayt içeriği (resimler veya diğer ikili dosyalar için).
* response.url: İsteğin yapıldığı son URL (yönlendirmelerden sonra).
* response.headers: Yanıt başlıkları.
* response.json(): Eğer yanıt bir JSON formatındaysa, bunu bir Python sözlüğüne dönüştürür.
Sorgu Parametreleri ile GET İstekleri
Birçok web sitesi, URL’deki sorgu parametreleri (?key=value&another_key=another_value) aracılığıyla verileri filtrelemeye veya aramaya olanak tanır. Requests bunu params argümanı ile kolaylaştırır.
import requests
url = "https://www.google.com/search"
params = {
"q": "python web scraping",
"hl": "tr" # Dil parametresi
}
response = requests.get(url, params=params)
if response.status_code == 200:
print(f"Google araması için URL: {response.url}")
# print(response.text) # Arama sonuçlarını gösterir
else:
print(f"İstek başarısız oldu. Durum kodu: {response.status_code}")
Başlıklar (Headers) Gönderme
Bazı web siteleri, belirli başlıklar olmadan isteklere yanıt vermez veya farklı yanıtlar verir. Özellikle User-Agent başlığı, bir bot olduğunuzu gizlemek (veya en azından normal bir tarayıcı gibi görünmek) için önemlidir.
import requests
url = "https://httpbin.org/headers" # Gönderilen başlıkları gösteren test sitesi
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",
"Accept-Language": "tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
print("Gönderilen başlıklar:")
print(response.json()['headers'])
else:
print(f"İstek başarısız oldu. Durum kodu: {response.status_code}")
POST İstekleri
Form gönderme veya API’ye veri yazma gibi işlemler için POST istekleri kullanılır. Veriler genellikle isteğin gövdesinde (body) gönderilir.
Form Verileri Gönderme
HTML formları genellikle application/x-www-form-urlencoded formatında veri gönderir. Requests bunu data argümanı ile kolayca halleder.
import requests
url = "https://httpbin.org/post"
payload = {
"username": "myuser",
"password": "mypassword"
}
response = requests.post(url, data=payload)
if response.status_code == 200:
print("POST isteği başarılı!")
print(response.json()['form'])
else:
print(f"İstek başarısız oldu. Durum kodu: {response.status_code}")
JSON Verileri Gönderme
API’lerle çalışırken genellikle JSON formatında veri göndermek gerekir. Requests bunu json argümanı ile otomatik olarak halleder ve Content-Type: application/json başlığını ayarlar.
import requests
url = "https://httpbin.org/post"
payload = {
"name": "Alice",
"age": 30,
"city": "New York"
}
response = requests.post(url, json=payload)
if response.status_code == 200:
print("JSON POST isteği başarılı!")
print(response.json()['json'])
else:
print(f"İstek başarısız oldu. Durum kodu: {response.status_code}")
Zaman Aşımı ve Hata Yönetimi
Ağ istekleri başarısız olabilir veya çok uzun sürebilir. timeout parametresi ve try-except blokları ile bu durumları yönetmek önemlidir.
import requests
url = "https://www.google.com" # Geçerli bir URL
url = "http://nonexistent-domain-12345.com" # Geçersiz bir URL
try:
response = requests.get(url, timeout=5) # 5 saniye zaman aşımı
response.raise_for_status() # HTTP hataları için istisna fırlatır (örn. 404, 500)
print("İstek başarılı!")
# print(response.text)
except requests.exceptions.Timeout:
print("İstek zaman aşımına uğradı.")
except requests.exceptions.HTTPError as err:
print(f"HTTP Hatası: {err}")
except requests.exceptions.RequestException as err:
print(f"Genel bir Requests hatası oluştu: {err}")
Session Kullanımı
Birden fazla istek yaparken çerezleri (cookies) ve oturum bilgilerini korumak için Session nesnesi kullanmak faydalıdır. Bu, özellikle oturum açma gerektiren sitelerde veya birden fazla sayfadan veri çekerken önemlidir.
import requests
Bir Session nesnesi oluşturma
session = requests.Session()
İlk istekte çerezler alınır ve session'da saklanır
response1 = session.get("https://httpbin.org/cookies/set/mycookie/myvalue")
print(f"İlk isteğin çerezleri: {session.cookies.get_dict()}")
İkinci istekte, session'da saklanan çerezler otomatik olarak gönderilir
response2 = session.get("https://httpbin.org/cookies")
print(f"İkinci isteğin gönderdiği çerezler: {response2.json()['cookies']}")
Beautiful Soup ile HTML Ayrıştırma
Requests ile bir web sayfasının HTML içeriğini aldıktan sonra, bu metin içindeki spesifik verileri bulmak ve çıkarmak için Beautiful Soup‘a ihtiyaç duyarız.
Giriş
Beautiful Soup, HTML ve XML dosyalarından veri çekmek için tasarlanmış bir Python kütüphanesidir. Ayrıştırılan içeriği bir ağaç yapısına dönüştürerek, etiketler, nitelikler ve metin içeriği arasında kolayca gezinmemizi ve arama yapmamızı sağlar.
Kurulum
Beautiful Soup‘u kurmak için pip kullanın. Ayrıca, HTML’i ayrıştırmak için bir ayrıştırıcıya (parser) ihtiyacımız var. lxml genellikle en hızlı ve en esnek ayrıştırıcıdır.
pip install beautifulsoup4 lxml
Soup Nesnesi Oluşturma
Beautiful Soup kullanmak için, önce HTML içeriğini bir BeautifulSoup nesnesine dönüştürmemiz gerekir.
from bs4 import BeautifulSoup
import requests
url = "https://www.example.com"
response = requests.get(url)
if response.status_code == 200:
html_content = response.text
# BeautifulSoup nesnesi oluşturma
# 'lxml' ayrıştırıcısını kullanıyoruz
soup = BeautifulSoup(html_content, 'lxml')
# Sayfanın başlığını (title) yazdırma
print(f"Sayfa Başlığı: {soup.title.string}")
else:
print(f"İstek başarısız oldu. Durum kodu: {response.status_code}")
Temel Eleman Seçimi
Beautiful Soup, HTML ağacında gezinmek ve elemanları bulmak için çeşitli yöntemler sunar.
Tag İsimleriyle Arama
Doğrudan tag isimlerini kullanarak ilk eşleşen elemana erişebilirsiniz.
# Yukarıdaki soup nesnesini kullanarak devam ediyoruz
print(f"İlk p etiketi: {soup.p}")
print(f"İlk p etiketinin metni: {soup.p.string}")
find() Metodu
find() metodu, belirtilen kriterlere uyan ilk elemanı döndürür.
# İlk
Bu bir giriş paragrafıdır.
İletişim
