Web Scraping: BeautifulSoup ve Selenium
İnternetteki bilginin büyük çoğunluğu API olarak sunulmuyor. Haber siteleri, e-ticaret platformları, istatistik sayfaları — hepsi bilgiyi HTML formatında, insanların okuması için sunuyor. Peki bu veriyi programatik olarak çekmek istersen ne yaparsın? İşte tam burada web scraping devreye giriyor.
Bu derste Python'un iki büyük scraping aracını — BeautifulSoup ve Selenium — sıfırdan öğreneceksin. Statik sayfalardan veri çekecek, JavaScript ile render edilen dinamik sayfalarla başa çıkacak ve bunu yaparken etik sınırları bileceksin.
1. Web Scraping Nedir?
🕷️ Analoji: Kütüphanedeki Stajyer
Bir kütüphanede çalışan stajyer düşün. Müdür ona diyor ki: "Git tüm kitapların adını, yazarını ve raf numarasını bir Excel tablosuna yaz." Stajyer her rafa gidiyor, kitap bilgilerini okuyor ve tabloya yazıyor. Yorucu ama sistematik bir iş.
Web scraping tam olarak bu. Bir program, web sayfalarını ziyaret eder, HTML yapısını okur ve istediğin bilgiyi çıkarıp yapılandırılmış hale getirir. Stajyer yerine bir Python scripti çalışıyor — hem çok daha hızlı, hem 7/24 çalışabilir.
Ne Zaman Kullanılır?
Scraping'e başvurmanın meşru nedenleri var:
API yok: Site veriyi sadece HTML olarak sunuyor, bir REST API sağlamıyor
Veri toplama: Fiyat karşılaştırma, haber takibi, akademik araştırma
Otomasyon: Belirli bir sayfadaki değişiklikleri takip etme
Veri göçü: Eski bir sistemdeki veriyi yeni sisteme aktarma
Ama her zaman önce API var mı diye kontrol et. API varsa scraping yapma — API hem daha güvenilir hem daha hızlıdır.
Etik Kurallar ve robots.txt
Scraping güçlü bir araç ama sorumluluk gerektirir. Birkaç temel kural:
robots.txt: Her ciddi site kök dizininde bir robots.txt dosyası barındırır. Bu dosya hangi sayfaların taranabileceğini, hangilerinin yasaklandığını belirtir.
# https://example.com/robots.txt
User-agent: *
Disallow: /admin/
Disallow: /private/
Crawl-delay: 10Bu dosya "lütfen /admin/ ve /private/ dizinlerini taramayın, istekler arasında 10 saniye bekleyin" diyor. Yasal olarak bağlayıcılığı tartışmalı olsa da, etik bir scraper robots.txt'e uyar.
Rate limiting: Sunucuyu boğma. İstekler arasında 1-2 saniye bekle. Bir siteye saniyede yüzlerce istek atarsan, DoS saldırısı yapmış olursun — hem etik dışı hem de IP'nin banlanır.
Kullanım şartları: Sitenin Terms of Service (ToS) sayfasını kontrol et. Bazı siteler scraping'i açıkça yasaklar. Bu yasaklara uymamak hukuki sorunlara yol açabilir.
2. Kurulum
Bu derste birkaç kütüphaneye ihtiyacın olacak:
# Temel scraping
pip install requests beautifulsoup4
# Tablo verisi için
pip install pandas lxml
# Dinamik sayfalar için
pip install seleniumbeautifulsoup4 paketi kurulur ama import ederken bs4 olarak kullanılır. Bu küçük detay birçok kişiyi şaşırtır.
3. requests + BeautifulSoup ile Statik Scraping
Statik sayfalar, sunucu tarafında tamamen render edilmiş HTML döndüren sayfalardır. JavaScript çalışmasına gerek yoktur — HTML'i indirdiğinde tüm veri zaten oradadır.
İlk Scraping: Basit Bir Sayfa
import requests
from bs4 import BeautifulSoup
# Sayfayı indir
url = "https://quotes.toscrape.com/"
response = requests.get(url)
# HTTP hatasını kontrol et
response.raise_for_status()
# HTML'i parse et
soup = BeautifulSoup(response.text, "html.parser")
# Sayfa başlığını al
print(f"Sayfa: {soup.title.string}")
# Tüm alıntıları bul
quotes = soup.find_all("div", class_="quote")
for quote in quotes[:5]:
text = quote.find("span", class_="text").get_text()
author = quote.find("small", class_="author").get_text()
print(f" {author}: {text[:60]}...")İş akışı her zaman aynıdır: İndir → Parse et → Ara → Çıkar. requests indirme işini, BeautifulSoup ise parse ve arama işini yapar.
HTML Parser Seçimi
BeautifulSoup birden fazla parser destekler:
| Parser | Kurulum | Hız | Tolerans |
|---|---|---|---|
html.parser | Yerleşik | Orta | İyi |
lxml | pip install lxml | Hızlı | Çok iyi |
html5lib | pip install html5lib | Yavaş | Mükemmel |
Çoğu durumda html.parser yeterli. Hız önemliyse veya bozuk HTML ile uğraşıyorsan lxml kullan. html5lib en yavaş ama tarayıcının parse edeceği gibi parse eder — son çare olarak düşün.
4. HTML Parse: find(), find_all() ve CSS Selectors
BeautifulSoup'un gücü arama yeteneklerinde yatıyor. HTML ağacında istediğin elemana ulaşmanın birden fazla yolu var.
find() ve find_all()
find() ilk eşleşeni, find_all() tüm eşleşenleri döndürür:
from bs4 import BeautifulSoup
html = """
<html>
<body>
<h1>Başlık</h1>
<div class="container">
<p class="intro">İlk paragraf</p>
<p class="content">İkinci paragraf</p>
<p class="content" id="special">Üçüncü paragraf</p>
</div>
<a href="https://example.com">Link 1</a>
<a href="https://python.org" class="external">Link 2</a>
</body>
</html>
"""
soup = BeautifulSoup(html, "html.parser")
# Tag adıyla arama
h1 = soup.find("h1")
print(h1.string) # "Başlık"
# Class ile arama
intro = soup.find("p", class_="intro")
print(intro.get_text()) # "İlk paragraf"
# Birden fazla sonuç
all_content = soup.find_all("p", class_="content")
print(len(all_content)) # 2
# ID ile arama
special = soup.find("p", id="special")
print(special.string) # "Üçüncü paragraf"
# Attribute ile arama
external_links = soup.find_all("a", attrs={"class": "external"})
print(external_links[0]["href"]) # "https://python.org"class_ parametresindeki alt çizgiye dikkat — class Python'da ayrılmış bir kelime olduğu için BeautifulSoup class_ kullanır.
CSS Selectors ile Arama
CSS selector'lara aşinaysan select() metodu çok daha doğal gelecektir:
from bs4 import BeautifulSoup
html = """
<div class="products">
<div class="product" data-price="99">
<h3>Laptop</h3>
<span class="price">99 TL</span>
</div>
<div class="product" data-price="49">
<h3>Mouse</h3>
<span class="price">49 TL</span>
</div>
<div class="product featured" data-price="199">
<h3>Monitor</h3>
<span class="price">199 TL</span>
</div>
</div>
"""
soup = BeautifulSoup(html, "html.parser")
# Tag seçimi
titles = soup.select("h3") # Tüm h3'ler
# Class seçimi
prices = soup.select(".price") # class="price" olanlar
# İç içe seçim (descendant)
product_titles = soup.select(".products .product h3")
# Direkt çocuk (child)
direct_divs = soup.select(".products > div")
# Attribute seçimi
featured = soup.select(".product.featured")
print(featured[0].select_one("h3").string) # "Monitor"
# data-* attribute
expensive = soup.select('[data-price="199"]')
print(expensive[0].select_one("h3").string) # "Monitor"select() tüm eşleşenleri liste olarak döndürür, select_one() ilk eşleşeni döndürür. CSS selector söz dizimi web geliştirme deneyimin varsa çok tanıdık gelecektir.
Ağaçta Gezinme (Navigating the Tree)
Bazen bir elemandan başlayıp komşu veya üst/alt elemanlara gitmen gerekir:
from bs4 import BeautifulSoup
html = """
<table>
<tr>
<td>Ali</td>
<td>90</td>
</tr>
<tr>
<td>Ayşe</td>
<td>95</td>
</tr>
</table>
"""
soup = BeautifulSoup(html, "html.parser")
first_td = soup.find("td")
# Kardeşe git
print(first_td.find_next_sibling("td").string) # "90"
# Üst elemana git
row = first_td.parent # <tr> elementi
print(row.name) # "tr"
# Tüm çocukları gez
for child in row.children:
if child.name == "td":
print(child.string)
# Sonraki elemana atla
next_row = row.find_next_sibling("tr")
print(next_row.find("td").string) # "Ayşe"Bu navigasyon yöntemleri, karmaşık HTML yapılarında hedef elemana dolaylı yoldan ulaşman gerektiğinde kurtarıcıdır.
5. Tablo Verisi Çekme → pandas DataFrame
Scraping'in en yaygın kullanım alanlarından biri web sayfalarındaki tabloları veri analizi için çekmektir. Güzel haber: pandas bu işi neredeyse tek satırda yapabilir.
pandas.read_html() ile Hızlı Yol
import pandas as pd
# Wikipedia'dan tablo çekme
url = "https://en.wikipedia.org/wiki/List_of_countries_by_population_(United_Nations)"
# Sayfadaki TÜM tabloları çek
tables = pd.read_html(url)
print(f"Sayfada {len(tables)} tablo bulundu")
# İlk tabloyu incele
df = tables[0]
print(df.head())
print(f"Sütunlar: {list(df.columns)}")pd.read_html() sayfadaki tüm <table> elementlerini bulur ve her birini DataFrame'e çevirir. Çoğu zaman ihtiyacın olan tablo ilk birkaç sonuçtan biridir.
BeautifulSoup + pandas: Tam Kontrol
Bazen read_html() yeterli olmaz — tablo yapısı karmaşıktır veya özel bir parse mantığı gerekir:
import requests
from bs4 import BeautifulSoup
import pandas as pd
url = "https://quotes.toscrape.com/tableful/"
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
# Tabloyu bul
table = soup.find("table")
# Başlıkları çek
headers = []
header_row = table.find("thead")
if header_row:
headers = [th.get_text(strip=True) for th in header_row.find_all("th")]
# Satırları çek
rows = []
for tr in table.find("tbody").find_all("tr"):
cells = [td.get_text(strip=True) for td in tr.find_all("td")]
rows.append(cells)
# DataFrame oluştur
df = pd.DataFrame(rows, columns=headers if headers else None)
print(df.head())
print(f"\nSatır sayısı: {len(df)}")Bu yaklaşım daha fazla kod gerektiriyor ama tam kontrol sağlıyor. Hücrelerdeki linkleri, resimleri veya özel attribute'ları da çekebilirsin.
💡 İpucu:
get_text(strip=True)whitespace'leri temizler. Tablo hücrelerinde sıkça gereksiz boşluklar ve satır sonları olur —strip=Truebunları otomatik temizler.
6. Selenium ile Dinamik Sayfa Scraping
Modern web'in büyük kısmı JavaScript ile render ediliyor. Bir e-ticaret sitesinde ürün listesi sayfa yüklendiğinde gelmiyor — JavaScript çalışıyor, bir API'a istek atıyor ve sonuçları DOM'a ekliyor. requests ile bu sayfanın HTML'ini indirsen, boş bir iskelet görürsün.
Selenium gerçek bir tarayıcıyı programatik olarak kontrol eder. JavaScript çalışır, sayfalar render edilir, tıklama ve form doldurma yapabilirsin.
WebDriver Kurulumu
Selenium bir tarayıcı sürücüsüne (WebDriver) ihtiyaç duyar. Modern Selenium (4.6+) sürücüyü otomatik indirir:
pip install seleniumSelenium 4.6 ve üzeri sürümlerde webdriver-manager gibi harici paketlere gerek yok. Selenium'un kendi SeleniumManager'ı sürücüyü otomatik bulur ve indirir.
Temel Kullanım
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
# Chrome ayarları
options = Options()
options.add_argument("--headless=new") # Tarayıcı penceresi açma
# Tarayıcıyı başlat
driver = webdriver.Chrome(options=options)
try:
# Sayfaya git
driver.get("https://quotes.toscrape.com/js/")
# Sayfa başlığı
print(f"Başlık: {driver.title}")
# Elementleri bul
quotes = driver.find_elements(By.CSS_SELECTOR, ".quote")
for quote in quotes[:5]:
text = quote.find_element(By.CSS_SELECTOR, ".text").text
author = quote.find_element(By.CSS_SELECTOR, ".author").text
print(f" {author}: {text[:60]}...")
finally:
# Tarayıcıyı kapat — mutlaka yap!
driver.quit()/js/ endpoint'ine dikkat — bu sayfa JavaScript ile render ediliyor. requests ile çeksen alıntılar gelmez, Selenium ile çeksen gelir.
Element Bulma Yöntemleri
Selenium'da element bulmak için By sınıfı kullanılır:
from selenium.webdriver.common.by import By
# ID ile
element = driver.find_element(By.ID, "search-box")
# Class ile
elements = driver.find_elements(By.CLASS_NAME, "product")
# CSS selector ile
element = driver.find_element(By.CSS_SELECTOR, "div.container > h1")
# XPath ile
element = driver.find_element(By.XPATH, "//div[@class='price']")
# Tag adıyla
links = driver.find_elements(By.TAG_NAME, "a")
# Link metniyle
link = driver.find_element(By.LINK_TEXT, "Next →")find_element() tekil sonuç döndürür (bulamazsa hata fırlatır), find_elements() liste döndürür (bulamazsa boş liste).
Wait Stratejileri
Dinamik sayfalarda en büyük sorun zamanlama'dır. Sayfa yükleniyor ama JavaScript henüz çalışmadı — element arıyorsun, yok. Selenium bunu çözmek için iki wait mekanizması sunar:
Implicit Wait: Tüm element aramalarına genel bir bekleme süresi ekler:
driver.implicitly_wait(10) # Her aramada en fazla 10 saniye bekleBu yöntem basit ama kaba bir araç — her arama için geçerli olduğu için gereksiz yavaşlamaya neden olabilir.
Explicit Wait (Önerilen): Belirli bir koşulun gerçekleşmesini bekler:
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# Element görünür olana kadar en fazla 10 saniye bekle
wait = WebDriverWait(driver, 10)
element = wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".results"))
)
# Element tıklanabilir olana kadar bekle
button = wait.until(
EC.element_to_be_clickable((By.ID, "load-more"))
)
button.click()
# Belirli bir metin görünene kadar bekle
wait.until(
EC.text_to_be_present_in_element(
(By.CSS_SELECTOR, ".status"), "Yüklendi"
)
)Explicit wait'ler çok daha hassas kontrol sağlar. "Bu element DOM'da olana kadar bekle", "bu buton tıklanabilir olana kadar bekle" gibi spesifik koşullar tanımlarsın.
⚠️ Dikkat:
time.sleep()ile bekleme yapmaktan kaçın. Sabit süre beklemek hem yavaş hem güvenilmezdir — sayfa 0.5 saniyede yüklenebilir ama sen 5 saniye bekliyorsun, ya da 5 saniye yetmeyebilir.WebDriverWaitkoşul sağlanır sağlanmaz devam eder.
7. Headless Browser Modu
Headless mod, tarayıcıyı görünür bir pencere olmadan çalıştırır. Sunucuda (GUI olmayan ortam) scraping yaparken veya daha hızlı çalışmak istediğinde kullanırsın.
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless=new") # Yeni headless modu
options.add_argument("--no-sandbox") # Docker/CI ortamı için
options.add_argument("--disable-dev-shm-usage") # Bellek optimizasyonu
options.add_argument("--window-size=1920,1080") # Viewport boyutu
# User-Agent ayarla (bazı siteler headless tarayıcıları engeller)
options.add_argument(
"user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
)
driver = webdriver.Chrome(options=options)--headless=new Chrome 112+ ile gelen yeni headless modu. Eski --headless parametresine göre gerçek Chrome'a çok daha yakın davranır — bazı anti-bot sistemleri eski headless modu tespit edebiliyordu.
8. Anti-Scraping Önlemleri ve Etik Yaklaşım
Web siteleri scraping'e karşı çeşitli savunma mekanizmaları kullanır. Bunları bilmek hem kendin scraping yaparken hem de kendi siteni korurken işe yarar.
Yaygın Anti-Scraping Teknikleri
IP tabanlı rate limiting: Aynı IP'den çok sayıda istek gelirse engelleyin.
import time
import requests
def polite_scrape(urls, delay=2):
"""Nazik scraper — istekler arasında bekler."""
results = []
for url in urls:
response = requests.get(url, headers={
"User-Agent": "MyResearchBot/1.0 (contact@example.com)"
})
results.append(response)
time.sleep(delay) # Sunucuya nefes aldır
return resultsUser-Agent kontrolü: Bazı siteler bilinen bot User-Agent'larını engeller. Gerçekçi bir User-Agent kullanmak temel bir önlemdir — ama bunu kötü niyetli olarak değil, sitenin normal kullanıcılara sunduğu deneyimi yaşamak için yaparsın.
CAPTCHA: İnsan doğrulama sistemi. CAPTCHA ile karşılaşırsan, bu site senden scraping yapmamanı istiyor demektir. CAPTCHA'yı otomatik çözmeye çalışmak etik açıdan sorunludur.
Honeypot tuzakları: Normal kullanıcının göremeyeceği (CSS ile gizlenmiş) linkler. Bir bot bu linkleri takip ederse, IP'si tespit edilip engellenir.
Etik Scraping Rehberi
import requests
from urllib.robotparser import RobotFileParser
def check_robots_txt(base_url, path):
"""robots.txt kurallarını kontrol et."""
rp = RobotFileParser()
rp.set_url(f"{base_url}/robots.txt")
rp.read()
full_url = f"{base_url}{path}"
can_fetch = rp.can_fetch("*", full_url)
crawl_delay = rp.crawl_delay("*")
print(f"URL: {full_url}")
print(f"İzin: {'✅ Evet' if can_fetch else '❌ Hayır'}")
if crawl_delay:
print(f"Bekleme süresi: {crawl_delay} saniye")
return can_fetch
# Kullanım
allowed = check_robots_txt("https://example.com", "/products")
if allowed:
print("Scraping yapılabilir")
else:
print("Bu sayfa scraping'e kapalı")Python'un standart kütüphanesindeki RobotFileParser, robots.txt dosyasını parse edip belirli bir URL'nin taranıp taranamayacağını söyler. Profesyonel bir scraper her zaman önce bunu kontrol eder.
Session nesnesi (requests.Session()) cookie'leri, header'ları ve TCP bağlantılarını istekler arasında paylaşır — hem performansı artırır hem de sitenin seni normal bir kullanıcı olarak görmesini sağlar. Login gerektiren sitelerde session vazgeçilmezdir.
9. Gerçek Dünya Örneği: Haber Başlıkları Çekme
Tüm öğrendiklerimizi birleştirelim. Aşağıdaki örnek, bir haber sitesinden başlıkları çeker, yapılandırır ve kaydeder. Hem requests + BeautifulSoup hem Selenium versiyonunu görelim.
Versiyon 1: BeautifulSoup ile Statik Sayfa
"""
Haber başlıkları scraper — BeautifulSoup versiyonu.
Quotes to Scrape sitesini haber sitesi yerine kullanıyoruz (güvenli ve legal).
"""
import requests
from bs4 import BeautifulSoup
import csv
import time
from urllib.robotparser import RobotFileParser
from datetime import datetime
def check_permission(base_url):
"""robots.txt kontrolü."""
rp = RobotFileParser()
rp.set_url(f"{base_url}/robots.txt")
try:
rp.read()
return rp.can_fetch("*", base_url)
except Exception:
print("robots.txt okunamadı — dikkatli devam ediyoruz")
return True
def scrape_quotes(base_url, max_pages=3, delay=1.5):
"""Birden fazla sayfadan alıntı çeker."""
if not check_permission(base_url):
print("robots.txt izin vermiyor, çıkılıyor.")
return []
session = requests.Session()
session.headers.update({
"User-Agent": "EducationalBot/1.0 (learning-project)"
})
all_quotes = []
for page_num in range(1, max_pages + 1):
url = f"{base_url}/page/{page_num}/"
print(f"Sayfa {page_num} çekiliyor: {url}")
try:
response = session.get(url, timeout=10)
response.raise_for_status()
except requests.RequestException as e:
print(f" Hata: {e}")
break
soup = BeautifulSoup(response.text, "html.parser")
quotes = soup.find_all("div", class_="quote")
if not quotes:
print(" Alıntı bulunamadı — son sayfa.")
break
for quote in quotes:
text = quote.find("span", class_="text").get_text()
author = quote.find("small", class_="author").get_text()
tags = [tag.get_text() for tag in quote.find_all("a", class_="tag")]
all_quotes.append({
"text": text,
"author": author,
"tags": ", ".join(tags),
"page": page_num,
"scraped_at": datetime.now().isoformat()
})
print(f" {len(quotes)} alıntı bulundu")
# Sonraki sayfa var mı?
next_btn = soup.find("li", class_="next")
if not next_btn:
print(" Son sayfa — başka sayfa yok.")
break
time.sleep(delay) # Rate limiting — nazik ol
return all_quotes
def save_to_csv(quotes, filename="quotes.csv"):
"""Alıntıları CSV dosyasına kaydeder."""
if not quotes:
print("Kaydedilecek veri yok.")
return
fieldnames = ["text", "author", "tags", "page", "scraped_at"]
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(quotes)
print(f"\n{len(quotes)} alıntı '{filename}' dosyasına kaydedildi.")
# Çalıştır
quotes = scrape_quotes("https://quotes.toscrape.com", max_pages=3)
save_to_csv(quotes)
# Özet
if quotes:
authors = set(q["author"] for q in quotes)
print(f"\nÖzet: {len(quotes)} alıntı, {len(authors)} farklı yazar")Bu örnek üretim kalitesinde bir scraper'ın temel bileşenlerini gösteriyor: robots.txt kontrolü, session yönetimi, hata yakalama, rate limiting, sayfalama (pagination) ve veri kaydetme.
Versiyon 2: Selenium ile Dinamik Sayfa
"""
Haber başlıkları scraper — Selenium versiyonu.
JavaScript ile render edilen sayfalar için.
"""
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import json
from datetime import datetime
def create_driver():
"""Headless Chrome driver oluşturur."""
options = Options()
options.add_argument("--headless=new")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
return webdriver.Chrome(options=options)
def scrape_js_quotes(max_pages=3):
"""JavaScript ile render edilen sayfadan alıntı çeker."""
driver = create_driver()
wait = WebDriverWait(driver, 10)
all_quotes = []
try:
for page_num in range(1, max_pages + 1):
url = f"https://quotes.toscrape.com/js/page/{page_num}/"
print(f"Sayfa {page_num} çekiliyor: {url}")
driver.get(url)
# JavaScript render'ı tamamlanana kadar bekle
wait.until(
EC.presence_of_all_elements_located(
(By.CSS_SELECTOR, ".quote")
)
)
quotes = driver.find_elements(By.CSS_SELECTOR, ".quote")
for quote in quotes:
text = quote.find_element(By.CSS_SELECTOR, ".text").text
author = quote.find_element(By.CSS_SELECTOR, ".author").text
tag_elements = quote.find_elements(By.CSS_SELECTOR, ".tag")
tags = [tag.text for tag in tag_elements]
all_quotes.append({
"text": text,
"author": author,
"tags": tags,
"page": page_num,
"scraped_at": datetime.now().isoformat()
})
print(f" {len(quotes)} alıntı bulundu")
# Sonraki sayfa kontrolü
next_buttons = driver.find_elements(
By.CSS_SELECTOR, "li.next > a"
)
if not next_buttons:
print(" Son sayfa.")
break
finally:
driver.quit() # Tarayıcıyı her zaman kapat
return all_quotes
def save_to_json(quotes, filename="quotes_js.json"):
"""Alıntıları JSON dosyasına kaydeder."""
with open(filename, "w", encoding="utf-8") as f:
json.dump(quotes, f, ensure_ascii=False, indent=2)
print(f"\n{len(quotes)} alıntı '{filename}' dosyasına kaydedildi.")
# Çalıştır
quotes = scrape_js_quotes(max_pages=3)
save_to_json(quotes)İki versiyon arasındaki temel fark şu: BeautifulSoup versiyonu sadece HTTP isteği yapıyor (hızlı, hafif), Selenium versiyonu ise gerçek bir tarayıcı çalıştırıyor (yavaş, ağır ama JavaScript'i çalıştırabilir).
Hangisini Ne Zaman Kullanmalı?
| Durum | Araç | Neden |
|---|---|---|
| Statik HTML | requests + BS4 | Hızlı, az kaynak |
| JS-rendered sayfa | Selenium | JavaScript çalıştırması gerekli |
| Login gerekli (basit) | requests + Session | Cookie yönetimi yeterli |
| Login + JS | Selenium | Form doldurma + JS |
| Büyük ölçek (binlerce sayfa) | Scrapy | Asenkron, pipeline desteği |
💡 İpucu: Her zaman en hafif aracı seçmeye çalış. Sayfa statik mi, dinamik mi bilmiyorsan önce
requestsile dene — içeriği gelen HTML'de göremiyorsan Selenium'a geç.
10. İleri Teknikler
Sayfalama (Pagination) ile Tüm Veriyi Çekme
Çoğu site veriyi sayfalara böler. Tüm sayfaları gezmek için sayfalama mantığı kurman gerekir:
import requests
from bs4 import BeautifulSoup
import time
def scrape_all_pages(base_url):
"""Tüm sayfaları otomatik gezen scraper."""
all_data = []
page = 1
while True:
url = f"{base_url}/page/{page}/"
response = requests.get(url)
if response.status_code == 404:
break
soup = BeautifulSoup(response.text, "html.parser")
items = soup.select(".quote")
if not items:
break
for item in items:
all_data.append({
"text": item.select_one(".text").get_text(),
"author": item.select_one(".author").get_text(),
})
print(f"Sayfa {page}: {len(items)} öğe")
# Sonraki sayfa var mı?
if not soup.select_one("li.next"):
break
page += 1
time.sleep(1)
return all_data
data = scrape_all_pages("https://quotes.toscrape.com")
print(f"Toplam: {len(data)} alıntı")Hata Yönetimi ve Retry
Gerçek dünyada ağ hataları, zaman aşımları ve geçici sunucu sorunları yaşanır. Sağlam bir scraper bunlara hazırlıklı olmalı:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_resilient_session():
"""Otomatik retry mekanizmalı session oluşturur."""
session = requests.Session()
retry_strategy = Retry(
total=3, # Toplam deneme sayısı
backoff_factor=1, # Denemeler arası bekleme: 1s, 2s, 4s
status_forcelist=[429, 500, 502, 503, 504], # Hangi status'larda tekrarla
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.headers.update({
"User-Agent": "EducationalBot/1.0"
})
return session
# Kullanım
session = create_resilient_session()
response = session.get("https://quotes.toscrape.com/", timeout=10)
print(f"Status: {response.status_code}")Retry sınıfı, belirli HTTP durum kodlarında isteği otomatik tekrarlar. backoff_factor=1 ile denemeler arasında artan bekleme süreleri uygulanır (1s, 2s, 4s) — bu hem sunucuyu rahatlatır hem de geçici sorunların çözülmesine zaman tanır.
Özet
Web scraping, web sayfalarından programatik olarak veri çekme işlemidir — API olmadığında başvurulur
requests + BeautifulSoup statik sayfalar için hafif ve hızlı çözüm;
find(),find_all(),select()ile HTML ağacında arama yaparsınSelenium JavaScript ile render edilen dinamik sayfalar için gerçek tarayıcı otomasyonu sağlar;
WebDriverWaitile akıllı bekleme yaparsınpandas.read_html() web sayfalarındaki tabloları tek satırda DataFrame'e çevirir
Etik scraping robots.txt'e uymak, rate limiting uygulamak ve sitenin kullanım şartlarına saygı göstermek demektir
Her zaman en hafif aracı seç — önce API, sonra requests + BS4, en son Selenium veya Scrapy
AI Asistan
Sorularını yanıtlamaya hazır