Python’da 30 satır kod yazıp Goodreads scraper’ınızı çalıştırdıktan sonra karşınıza [] çıktığını görmekten daha moral bozucu çok az şey vardır. Boş bir liste. Hiçbir şey. Sadece sen ve yanıp sönen imleç.
Bunu defalarca gördüm — Thunderbit’teki kendi iç testlerimizde, geliştirici forumlarında ve terk edilmiş scraper depolarının GitHub issue’larında. Şikayetler neredeyse hep aynı: "en üstteki yorumlar bölümü boş, sadece [] gösteriyor", "hangi sayfa numarasını çalıştırırsam çalıştırayım hep ilk sayfayı kazıyor", "kodum geçen yıl çalışıyordu, şimdi bozuldu." Üstelik Goodreads API’si Aralık 2020’de kullanım dışı bırakıldığı için eski eğitimlerde göreceğiniz “sadece API’yi kullan” tavsiyesi de artık çıkmaz sokak.
Bugün Goodreads’ten yapılandırılmış kitap verisi almak istiyorsan — başlıklar, yazarlar, puanlar, yorumlar, türler, ISBN’ler — işin merkezinde scraping var. Bu rehber, Python ile Goodreads’i başarıyla ve eksiksiz şekilde kazımak için gerekli yöntemi adım adım gösterecek; JS ile render edilen içerik, sayfalama, bloklanmaya karşı önlemler ve dışa aktarma konularını ele alacak. Python sana göre değilse, işi iki tıklamada halleden kodsuz bir alternatifi de paylaşacağım.
Goodreads Kazıma Nedir (ve Neden Python ile Yapılır)?
Goodreads kazıma, kitap verilerini — başlıklar, yazarlar, puanlar, yorum sayıları, türler, ISBN’ler, sayfa sayıları, yayın tarihleri ve daha fazlasını — elle kopyalayıp yapıştırmak yerine kod kullanarak Goodreads web sayfalarından otomatik biçimde çekmek demektir.
Goodreads, dünyanın en büyük kitap veri tabanlarından biridir; ve yaklaşık bulunur. Her ay 18 milyondan fazla kitap "Want to Read" rafına eklenir. Sürekli güncellenen, yapılandırılmış veri tam da bu yüzden yayıncıların, veri bilimcilerin, kitap satıcılarının ve araştırmacıların tekrar tekrar geri döndüğü bir kaynak olur.
Python bu iş için en çok tercih edilen dildir — tüm scraping projelerinin yaklaşık oranında arkasında Python vardır. Kütüphaneleri olgunlaşmıştır (requests, BeautifulSoup, Selenium, Playwright, pandas), sözdizimi başlangıç seviyesindekiler için bile uygundur ve topluluğu çok büyüktür.
Daha önce hiç site kazımadıysan, Python ile başlamak en doğru seçenektir.
Neden Goodreads’i Python ile Kazımalısınız? Gerçek Kullanım Senaryoları
Koda geçmeden önce şu soruyu sormakta fayda var: Bu veriye aslında kim ihtiyaç duyuyor ve ne için kullanıyor?
| Kullanım Senaryosu | Kimler İçin Faydalı | Neleri Kazırsınız |
|---|---|---|
| Yayıncı pazar araştırması | Yayıncılar, edebiyat ajansları | Popüler türler, en yüksek puanlı kitaplar, yükselen yazarlar, rakip puanları |
| Kitap öneri sistemleri | Veri bilimciler, hobi amaçlı kullanıcılar, uygulama geliştiriciler | Puanlar, türler, kullanıcı rafları, yorum duyarlılığı |
| Fiyat ve stok takibi | E-ticaret kitap satıcıları | Trend olan kitaplar, yorum hacmi, "Want to Read" sayıları |
| Akademik araştırma | Araştırmacılar, öğrenciler | Yorum metni, puan dağılımı, tür sınıflandırmaları |
| Okuma analitiği | Kitap blog yazarları, kişisel projeler | Kişisel raf verileri, okuma geçmişi, yıl sonu istatistikleri |
Somut örnekler de var: UCSD Book Graph — öneri sistemleri araştırmalarında en çok atıf alan akademik veri kümelerinden biri — Goodreads’in herkese açık raflarından toplanmış içeriyor. Birçok Kaggle veri seti (goodbooks-10k, Best Books Ever vb.) Goodreads kazımadan doğdu. Ve Big Data and Society dergisinde yayımlanan 2025 tarihli bir çalışma, sponsorlu yorumların platformu nasıl şekillendirdiğini analiz etmek için derledi.
Ticari tarafta ise Bright Data, önceden kazınmış Goodreads veri setlerini 1.000 kayıt başına sadece 0,50 dolardan satıyor — bu da verinin gerçek bir piyasa değerine sahip olduğunun kanıtı.
Goodreads API’si Artık Yok — Yerine Ne Geldi?
Son zamanlarda "Goodreads API" diye aradıysan, büyük ihtimalle güncel olmayan bir rehbere denk gelmişsindir. 8 Aralık 2020’de Goodreads sessizce yeni geliştirici API anahtarları vermeyi durdurdu. Blog yazısı yoktu, toplu e-posta da yoktu — sadece dokümantasyon sayfasında küçük bir uyarı ve kafası karışmış geliştiriciler.

Etkisi hemen hissedildi. Kyle K adlı bir geliştirici kitap önerileri paylaşan bir Discord botu yapmıştı — "bir anda POOF, çalışmayı kesti." Matthew Jones ise Reddit r/Fantasy Stabby Awards oylamasından bir hafta önce API erişimini kaybetti ve Google Forms’a geri dönmek zorunda kaldı. Elena Neacsu adlı bir lisansüstü öğrencisinin yüksek lisans tez projesi ise geliştirme sürecinin ortasında sekteye uğradı.
Peki geriye ne kaldı? Bugünkü tablo şu şekilde:
| Yöntem | Elde Edilen Veri | Kullanım Kolaylığı | Hız Sınırı | Durum |
|---|---|---|---|---|
| Goodreads API | Tüm metadata, yorumlar | Kolaydı | 1 istek/sn | Kullanım dışı (Ara 2020) — yeni anahtar yok |
| Open Library API | Başlıklar, yazarlar, ISBN’ler, kapaklar (~30M başlık) | Kolay | 1-3 istek/sn | Aktif, ücretsiz, kimlik doğrulama gerektirmez |
| Google Books API | Metadata, önizlemeler | Kolay | Günde 1.000 ücretsiz | Aktif (İngilizce dışı ISBN boşlukları var) |
| Python scraping (requests + BS4) | İlk HTML’de bulunan her şey | Orta | Siz yönetirsiniz | Statik içerik için çalışır |
| Python scraping (Selenium/Playwright) | JS ile render edilen içerik de dahil | Daha zor | Siz yönetirsiniz | Yorumlar, bazı listeler için gerekli |
| Thunderbit (kodsuz Chrome eklentisi) | Görünen herhangi bir sayfa verisi | Çok kolay (2 tık) | Kredi bazlı | Aktif — Python gerekmez |
Open Library güçlü bir tamamlayıcıdır, özellikle ISBN aramaları ve temel metadata için. Ancak puanlar, yorumlar, tür etiketleri ya da "Want to Read" sayıları gerekiyorsa, doğrudan Goodreads’i kazıman gerekir — ya Python ile ya da Thunderbit gibi bir araçla. Thunderbit, yapay zekâ önerili alanlar ve Google Sheets, Notion veya Airtable’a doğrudan dışa aktarma desteğiyle Goodreads sayfalarını (kitap detayları için alt sayfalar dahil) kazıyabilir.
Goodreads Python Scraper’ınız Neden Boş Sonuç Döndürüyor? (ve Nasıl Düzeltilir)
Goodreads verisiyle ilk çalışmaya başladığımda keşke bu bölüm olsaydı dedirten kısım burası. "Boş sonuç" problemi geliştirici forumlarında en sık görülen şikâyet ve bunun birkaç farklı nedeni var — her birinin de ayrı çözümü bulunuyor.
| Belirti | Kök Neden | Çözüm |
|---|---|---|---|
| Yorumlar/puanlar [] döndürüyor | JS ile render edilen içerik (React/lazy-load) | requests yerine Selenium veya Playwright kullanın |
| Hep sadece 1. sayfayı kazıyor | Sayfalama parametresi yok sayılıyor veya JS ile yönetiliyor | ?page=N parametresini doğru iletin; sonsuz kaydırma için tarayıcı otomasyonu kullanın |
| Kod geçen yıl çalışıyordu, şimdi çalışmıyor | Goodreads HTML class adlarını değiştirdi | Daha dayanıklı seçiciler kullanın (JSON-LD, data-testid özellikleri) |
| Birkaç istektan sonra 403/blok | Eksik header / çok hızlı istek | User-Agent ekleyin, time.sleep() kullanın, proxy döndürün |
| Raf/liste sayfalarında giriş duvarı | Çerez/oturum gerekiyor | Çerezlerle birlikte requests.Session() kullanın veya tarayıcıyla kazıyın |
JS ile Render Edilen İçerik: Yorumlar ve Puanlar Boş Geliyor
Goodreads, React tabanlı bir ön yüz kullanıyor. Bir kitap sayfasına requests.get() ile gittiğinizde yalnızca başlangıç HTML’ini alırsınız — yorumlar, puan dağılımları ve birçok "daha fazla bilgi" bölümü JavaScript ile sonradan yüklenir. Scraper’ınız bunları kelimenin tam anlamıyla göremez.
Çözüm: JS ile render edilen içeriğe ihtiyaç duyduğunuz her sayfada Selenium veya Playwright’a geçin. Yeni projeler için Playwright’ı öneririm — WebSocket tabanlı protokolü sayesinde ve yerleşik gizlilik ile async desteği daha iyidir.

Sadece 1. Sayfayı Döndüren Sayfalama
Bu oldukça sinsi bir durumdur. Bir döngü yazarsınız, ?page=N değerini artırırsınız, ama sonuç hep aynı kalır. Goodreads’te, giriş yapılmamışsa raf sayfaları sessizce ?page= parametresine rağmen 1. sayfa içeriğini döndürebilir. Hata yok, yönlendirme yok — sadece aynı ilk sayfa tekrar tekrar.
Çözüm: kimliği doğrulanmış bir oturum çerezi ekleyin (özellikle _session_id2). Bunu sayfalama bölümünde birazdan detaylandıracağım.
Geçen Yıl Çalışan Kodun Şimdi Bozulması
Goodreads zaman zaman HTML class adlarını ve sayfa yapısını değiştiriyor. GitHub’daki popüler maria-antoniak/goodreads-scraper deposunda artık kalıcı bir uyarı var: "Bu proje bakımsızdır ve artık çalışmamaktadır." Çözüm, daha dayanıklı seçiciler kullanmaktır — kırılgan class adları yerine JSON-LD yapılandırılmış verisi (schema.org standardına göre gelir ve nadiren değişir) ya da data-testid öznitelikleri.
403 Hataları veya Bloklanma
Python’un requests kütüphanesi Chrome’dan farklı bir TLS parmak izi kullanır. Chrome User-Agent ekleseniz bile, AWS WAF gibi bot tespit sistemleri (Goodreads bir Amazon iştiraki olduğu için kullanıyor) bu uyumsuzluğu fark edebilir. Çözüm: gerçekçi tarayıcı header’ları ekleyin, istekler arasında 3-8 saniyelik time.sleep() gecikmeleri koyun ve büyük ölçekli kazıma yapıyorsanız TLS parmak izi eşleşmesi için curl_cffi kullanmayı değerlendirin.
Raf ve Liste Sayfalarında Giriş Duvarı
Bazı Goodreads raf ve liste sayfaları, özellikle 5. sayfadan sonra, tam içeriğe erişmek için giriş doğrulaması ister. Tarayıcınızdan dışa aktarılmış çerezlerle birlikte requests.Session() kullanın ya da giriş yapılmış bir profil ile Selenium/Playwright çalıştırın. Thunderbit ise kendi giriş yapmış Chrome tarayıcın içinde çalıştığı için bunu doğal olarak halleder.
Başlamadan Önce
- Zorluk seviyesi: Orta (temel Python bilgisi varsayılır)
- Gerekli süre: Tam anlatım için yaklaşık 20-30 dakika
- İhtiyacın olacaklar:
- Python 3.8+
- Chrome tarayıcısı (DevTools incelemesi ve Selenium/Playwright için)
- Kütüphaneler:
requests,beautifulsoup4,seleniumveyaplaywright,pandas - (İsteğe bağlı) Google Sheets dışa aktarma için
gspread - (İsteğe bağlı) kodsuz alternatif için

1. Adım: Python Ortamınızı Kurun
Gerekli kütüphaneleri yükleyin. Terminalinizi açın ve şu komutu çalıştırın:
1pip install requests beautifulsoup4 selenium pandas lxml
Playwright tercih ediyorsanız (yeni projeler için önerilir):
1pip install playwright
2playwright install chromium
Google Sheets’e aktarmak için (isteğe bağlı):
1pip install gspread oauth2client
Python sürümünüzün 3.8 veya üzeri olduğundan emin olun. python --version ile kontrol edebilirsiniz.
Kurulumdan sonra tüm kütüphaneleri hatasız içe aktarabilmeniz gerekir. Doğrulamak için python -c "import requests, bs4, pandas; print('Ready')" komutunu deneyin.
2. Adım: Uygun Header’larla İlk İsteği Gönderin
Tarayıcınızda bir Goodreads genre shelf ya da liste sayfasını açın — örneğin https://www.goodreads.com/list/show/1.Best_Books_Ever. Şimdi bu sayfayı Python ile çekelim.
1import requests
2from bs4 import BeautifulSoup
3headers = {
4 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
5 "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
6 "Accept-Language": "en-US,en;q=0.9",
7 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
8}
9url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
10response = requests.get(url, headers=headers, timeout=15)
11print(f"Status: {response.status_code}")
Status: 200 görmelisin. Eğer 403 alırsan, header’larını kontrol et — Goodreads’in AWS WAF sistemi gerçekçi bir User-Agent bekler ve yalın istekleri reddeder. Yukarıdaki header’lar gerçek bir Chrome oturumunu taklit eder.
3. Adım: Sayfayı İnceleyin ve Doğru Seçicileri Bulun
Goodreads liste sayfasında Chrome DevTools’u (F12) açın. Bir kitap başlığına sağ tıklayıp "Inspect" seçin. Her kitap girdisinin DOM yapısını göreceksiniz.
Liste sayfalarında her kitap genellikle itemtype="http://schema.org/Book" olan bir <tr> öğesi içinde yer alır. İçinde şunları bulursunuz:
- Başlık:
a.bookTitle(link metni başlıktır,hrefise kitap URL’sini verir) - Yazar:
a.authorName - Puan:
span.minirating(ortalama puan ve puan sayısını içerir) - Kapak görseli: kitap satırının içindeki
img
Tek tek kitap detay sayfalarında CSS seçicilerle uğraşmak yerine doğrudan JSON-LD’ye geçin. Goodreads, schema.org Book formatını takip eden bir <script type="application/ld+json"> etiketi içinde yapılandırılmış veri gömer. Goodreads’in canı istediğinde değiştirdiği class adlarına göre çok daha stabildir.
4. Adım: Tek Bir Liste Sayfasından Kitap Verisini Çekin
Şimdi liste sayfasını ayrıştırıp her kitap için temel bilgileri alalım:
1import requests
2from bs4 import BeautifulSoup
3headers = {
4 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
5 "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
6 "Accept-Language": "en-US,en;q=0.9",
7}
8url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
9response = requests.get(url, headers=headers, timeout=15)
10soup = BeautifulSoup(response.text, "lxml")
11books = []
12rows = soup.select('tr[itemtype="http://schema.org/Book"]')
13for row in rows:
14 title_tag = row.select_one("a.bookTitle")
15 author_tag = row.select_one("a.authorName")
16 rating_tag = row.select_one("span.minirating")
17 title = title_tag.get_text(strip=True) if title_tag else ""
18 book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
19 author = author_tag.get_text(strip=True) if author_tag else ""
20 rating_text = rating_tag.get_text(strip=True) if rating_tag else ""
21 books.append({
22 "title": title,
23 "author": author,
24 "rating_info": rating_text,
25 "book_url": book_url,
26 })
27print(f"Page 1'de {len(books)} kitap bulundu")
28for b in books[:3]:
29 print(b)
Sayfa başına yaklaşık 100 kitap görmelisiniz. Her kayıtta başlık, yazar, "4.28 avg rating — 9,031,257 ratings" benzeri bir puan metni ve kitabın detay sayfasına giden URL yer alacaktır.
5. Adım: Ayrıntılı Kitap Bilgileri İçin Alt Sayfaları Kazıyın
Liste sayfası temel bilgileri verir; ama gerçek değer — ISBN, tam açıklama, tür etiketleri, sayfa sayısı, yayın tarihi — her kitabın kendi sayfasında bulunur. İşte JSON-LD’nin parladığı yer burasıdır.
1import json
2import time
3def scrape_book_detail(book_url, headers):
4 """Tek bir kitap sayfasını ziyaret eder ve JSON-LD üzerinden detaylı metadata çeker."""
5 resp = requests.get(book_url, headers=headers, timeout=15)
6 if resp.status_code != 200:
7 return {}
8 soup = BeautifulSoup(resp.text, "lxml")
9 script = soup.find("script", {"type": "application/ld+json"})
10 if not script:
11 return {}
12 data = json.loads(script.string)
13 agg = data.get("aggregateRating", {})
14 # Tür etiketleri JSON-LD içinde yok; HTML’den tamamlıyoruz
15 genres = [g.get_text(strip=True) for g in soup.select('span.BookPageMetadataSection__genreButton a span')]
16 return {
17 "isbn": data.get("isbn", ""),
18 "pages": data.get("numberOfPages", ""),
19 "language": data.get("inLanguage", ""),
20 "format": data.get("bookFormat", ""),
21 "avg_rating": agg.get("ratingValue", ""),
22 "rating_count": agg.get("ratingCount", ""),
23 "review_count": agg.get("reviewCount", ""),
24 "description": data.get("description", "")[:200], # önizleme için kısaltıldı
25 "genres": ", ".join(genres[:5]),
26 }
27# Örnek: ilk 3 kitabı zenginleştirelim
28for book in books[:3]:
29 details = scrape_book_detail(book["book_url"], headers)
30 book.update(details)
31 print(f"Kazındı: {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
32 time.sleep(4) # hız sınırlarına saygı gösterin
İstekler arasında 3-8 saniyelik time.sleep() kullanın. Goodreads’in hız sınırı tek bir IP’den dakikada yaklaşık 20-30 istek civarında devreye girer; daha hızlı giderseniz 403’ler veya CAPTCHA’larla karşılaşmaya başlarsınız.
Bu iki aşamalı yaklaşım — önce liste sayfalarından tüm kitap URL’lerini toplamak, sonra her detay sayfasını ziyaret etmek — daha güvenilir olur ve yarıda kesilse bile kolayca kaldığın yerden devam etmeni sağlar. Başarılı Goodreads scraper’larının çoğu bu stratejiyi kullanır.
Kısa not: bunu alt sayfa kazıma özelliğiyle otomatik yapabilir. Yapay zekâ her kitabın detay sayfasını ziyaret eder ve tablonu ISBN, açıklama, türler ve daha fazlasıyla zenginleştirir — kod yok, döngü yok, bekleme süresi yok.
6. Adım: JavaScript ile Render Edilen İçeriği Selenium ile İşleyin
Yorumlar, puan dökümleri, "daha fazla detay" bölümleri gibi JavaScript ile yüklenen içeriklere ihtiyaç duyduğunuz sayfalarda bir tarayıcı otomasyon aracına ihtiyacınız olacak. İşte Selenium örneği:
1from selenium import webdriver
2from selenium.webdriver.chrome.options import Options
3from selenium.webdriver.common.by import By
4from selenium.webdriver.support.ui import WebDriverWait
5from selenium.webdriver.support import expected_conditions as EC
6options = Options()
7options.add_argument("--headless=new")
8options.add_argument("--disable-blink-features=AutomationControlled")
9options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
10 "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
11driver = webdriver.Chrome(options=options)
12driver.get("https://www.goodreads.com/book/show/5907.The_Hobbit")
13# Yorumların yüklenmesini bekle
14try:
15 WebDriverWait(driver, 10).until(
16 EC.presence_of_element_located((By.CSS_SELECTOR, "article.ReviewCard"))
17 )
18except:
19 print("Yorumlar yüklenmedi — sayfa giriş gerektiriyor olabilir ya da JS zaman aşımına uğramış olabilir")
20# Şimdi tam render edilmiş sayfayı ayrıştır
21page_source = driver.page_source
22soup = BeautifulSoup(page_source, "lxml")
23reviews = soup.select("article.ReviewCard")
24for rev in reviews[:3]:
25 text = rev.select_one("span.Formatted")
26 stars = rev.select_one("span.RatingStars")
27 print(f"Puan: {stars.get_text(strip=True) if stars else 'N/A'}")
28 print(f"Yorum: {text.get_text(strip=True)[:150] if text else 'N/A'}...")
29 print()
30driver.quit()
requests mi Selenium mu ne zaman kullanılmalı:
- Kitap metadata’sı (JSON-LD), liste sayfaları, raf sayfaları (1. sayfa) ve Choice Awards verileri için
requests+ BeautifulSoup kullanın - Yorumlar, puan dağılımları ve ham HTML’de görünmeyen içerikler için Selenium veya Playwright kullanın
Yeni projeler için genellikle Playwright daha iyi bir tercihtir — daha hızlı, daha az bellek tüketir, gizlilik varsayılanları daha iyidir. Ancak Goodreads özelinde Selenium’un topluluğu daha büyük ve daha fazla örnek kod mevcuttur.
Gerçekten Çalışan Sayfalama: Tam Goodreads Listelerini Kazımak
Sayfalama, Goodreads scraper’larının en sık başarısız olduğu noktadır ve ben düzgün anlatan tek bir rakip rehbere rastlamadım. Doğru yöntem şu şekildedir.
Goodreads Sayfalama URL’leri Nasıl Çalışır?
Goodreads, çoğu sayfalı sayfa için basit bir ?page=N parametresi kullanır:
- Listeler:
https://www.goodreads.com/list/show/1.Best_Books_Ever?page=2 - Raflar:
https://www.goodreads.com/shelf/show/thriller?page=2 - Arama:
https://www.goodreads.com/search?q=fantasy&page=2
Her liste sayfası genellikle 100 kitap gösterir. Raflar sayfa başına 50 içerik gösterir.
Ne Zaman Duracağını Bilen Bir Sayfalama Döngüsü Yazmak
1import time
2all_books = []
3base_url = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
4for page_num in range(1, 50): # güvenlik için 50 sayfalık üst sınır
5 url = f"{base_url}?page={page_num}"
6 resp = requests.get(url, headers=headers, timeout=15)
7 if resp.status_code != 200:
8 print(f"Sayfa {page_num}: {resp.status_code} döndü, durduruluyor.")
9 break
10 soup = BeautifulSoup(resp.text, "lxml")
11 rows = soup.select('tr[itemtype="http://schema.org/Book"]')
12 if not rows:
13 print(f"Sayfa {page_num}: kitap bulunamadı, son sayfaya ulaşıldı.")
14 break
15 for row in rows:
16 title_tag = row.select_one("a.bookTitle")
17 author_tag = row.select_one("a.authorName")
18 title = title_tag.get_text(strip=True) if title_tag else ""
19 book_url = "https://www.goodreads.com" + title_tag["href"] if title_tag else ""
20 author = author_tag.get_text(strip=True) if author_tag else ""
21 all_books.append({"title": title, "author": author, "book_url": book_url})
22 print(f"Sayfa {page_num}: {len(rows)} kitap kazındı (toplam: {len(all_books)})")
23 time.sleep(5) # sayfalar arasında 5 saniye gecikme
24print(f"\nBitti. Toplam toplanan kitap sayısı: {len(all_books)}")
Son sayfayı, sonuç listesi boşsa (tr[itemtype="http://schema.org/Book"] öğesi yoksa) veya "next" bağlantısı (a.next_page) bulunmuyorsa anlarsınız.
5. Sayfadan Sonrası İçin Giriş Gerektiren Kenar Durum
Neredeyse herkesi tuzağa düşüren kısım budur: bazı Goodreads raf ve liste sayfaları, giriş yapılmadan 6. sayfa ve sonrası istendiğinde sessizce 1. sayfa içeriğini döndürür. Hata yok, yönlendirme yok — sadece aynı veri tekrar eder.
Bunu düzeltmek için tarayıcınızdan _session_id2 çerezini dışa aktarın (bir cookie export eklentisi ya da Chrome DevTools > Application > Cookies kullanın) ve isteklere ekleyin:
1session = requests.Session()
2session.headers.update(headers)
3session.cookies.set("_session_id2", "YOUR_SESSION_COOKIE_VALUE_HERE", domain=".goodreads.com")
4# Artık requests.get() yerine session.get() kullanın
5resp = session.get(f"{base_url}?page=6", timeout=15)
Thunderbit, kod yazmadan ve çerez yönetmeden, hem tıklamalı hem de sonsuz kaydırmalı sayfalamayı doğal olarak destekler. Sayfalama mantığın sürekli bozuluyorsa, değerlendirmeye değer.
Tam, Kopyala-Yapıştır Hazır Python Scripti
İşte eksiksiz ve birleştirilmiş script. Header’ları, sayfalama işlemini, JSON-LD üzerinden alt sayfa kazımayı, hız sınırlamasını ve CSV dışa aktarımını yönetir. Bunu 2025 ortası itibarıyla canlı Goodreads sayfalarında test ettim.
1"""
2goodreads_scraper.py — Goodreads listesini sayfalama ve kitap detayı zenginleştirmesiyle kazır.
3Kullanım: python goodreads_scraper.py
4Çıktı: goodreads_books.csv
5"""
6import csv, json, time, requests
7from bs4 import BeautifulSoup
8HEADERS = {
9 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
10 "(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
11 "Accept-Language": "en-US,en;q=0.9",
12 "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
13}
14BASE_URL = "https://www.goodreads.com/list/show/1.Best_Books_Ever"
15MAX_PAGES = 3 # ihtiyaca göre ayarlayın
16DELAY_LISTING = 5 # liste sayfaları arasında saniye
17DELAY_DETAIL = 4 # detay sayfaları arasında saniye
18OUTPUT_FILE = "goodreads_books.csv"
19def scrape_listing_page(url):
20 """Tek bir liste sayfasından title, author, book_url içeren dict listesi döndürür."""
21 resp = requests.get(url, headers=HEADERS, timeout=15)
22 if resp.status_code != 200:
23 return []
24 soup = BeautifulSoup(resp.text, "lxml")
25 rows = soup.select('tr[itemtype="http://schema.org/Book"]')
26 books = []
27 for row in rows:
28 t = row.select_one("a.bookTitle")
29 a = row.select_one("a.authorName")
30 if t:
31 books.append({
32 "title": t.get_text(strip=True),
33 "author": a.get_text(strip=True) if a else "",
34 "book_url": "https://www.goodreads.com" + t["href"],
35 })
36 return books
37def scrape_book_detail(book_url):
38 """Bir kitap sayfasını ziyaret eder ve JSON-LD + HTML yedeğiyle metadata çıkarır."""
39 resp = requests.get(book_url, headers=HEADERS, timeout=15)
40 if resp.status_code != 200:
41 return {}
42 soup = BeautifulSoup(resp.text, "lxml")
43 script = soup.find("script", {"type": "application/ld+json"})
44 if not script:
45 return {}
46 data = json.loads(script.string)
47 agg = data.get("aggregateRating", {})
48 genres = [g.get_text(strip=True)
49 for g in soup.select("span.BookPageMetadataSection__genreButton a span")]
50 return {
51 "isbn": data.get("isbn", ""),
52 "pages": data.get("numberOfPages", ""),
53 "avg_rating": agg.get("ratingValue", ""),
54 "rating_count": agg.get("ratingCount", ""),
55 "review_count": agg.get("reviewCount", ""),
56 "description": (data.get("description", "") or "")[:300],
57 "genres": ", ".join(genres[:5]),
58 "language": data.get("inLanguage", ""),
59 "format": data.get("bookFormat", ""),
60 "published": data.get("datePublished", ""),
61 }
62def main():
63 all_books = []
64 # --- Aşama 1: liste sayfalarından kitap URL’lerini topla ---
65 for page in range(1, MAX_PAGES + 1):
66 url = f"{BASE_URL}?page={page}"
67 page_books = scrape_listing_page(url)
68 if not page_books:
69 print(f"Sayfa {page}: boş — sayfalama durduruluyor.")
70 break
71 all_books.extend(page_books)
72 print(f"Sayfa {page}: {len(page_books)} kitap (toplam: {len(all_books)})")
73 time.sleep(DELAY_LISTING)
74 # --- Aşama 2: her kitabı detay sayfasından zenginleştir ---
75 for i, book in enumerate(all_books):
76 details = scrape_book_detail(book["book_url"])
77 book.update(details)
78 print(f"[{i+1}/{len(all_books)}] {book['title']} — ISBN: {book.get('isbn', 'N/A')}")
79 time.sleep(DELAY_DETAIL)
80 # --- CSV dışa aktar ---
81 if all_books:
82 fieldnames = list(all_books[0].keys())
83 with open(OUTPUT_FILE, "w", newline="", encoding="utf-8") as f:
84 writer = csv.DictWriter(f, fieldnames=fieldnames)
85 writer.writeheader()
86 writer.writerows(all_books)
87 print(f"\n{len(all_books)} kitap {OUTPUT_FILE} dosyasına kaydedildi")
88 else:
89 print("Hiç kitap kazınmadı.")
90if __name__ == "__main__":
91 main()
MAX_PAGES = 3 ile bu script, "Best Books Ever" listesinden yaklaşık 300 kitap toplar, her kitabın detay sayfasını ziyaret eder ve her şeyi CSV’ye yazar. Benim makinemde yaklaşık 25 dakika sürüyor (çoğu süre detay sayfaları arasındaki 4 saniyelik gecikmeden kaynaklanıyor). Çıktı CSV dosyanızda title, author, book_url, isbn, pages, avg_rating, rating_count, review_count, description, genres, language, format ve published gibi sütunlar olur.
CSV’nin Ötesinde Dışa Aktarım: gspread ile Google Sheets
Veriyi CSV yerine ya da CSV’ye ek olarak Google Sheets’e almak isterseniz, CSV dışa aktarımından sonra şunu ekleyin:
1import gspread
2from oauth2client.service_account import ServiceAccountCredentials
3scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
4creds = ServiceAccountCredentials.from_json_keyfile_name("credentials.json", scope)
5client = gspread.authorize(creds)
6sheet = client.open("Goodreads Scrape").sheet1
7header = list(all_books[0].keys())
8sheet.append_row(header)
9for book in all_books:
10 sheet.append_row([str(book.get(k, "")) for k in header])
11print("Veri Google Sheets’e aktarıldı.")
Sheets ve Drive API’leri etkin bir Google Cloud servis hesabına ihtiyacınız olacak. kurulumu yaklaşık 5 dakikada anlatıyor. Birkaç yüz satırdan fazlasını aktarıyorsanız toplu işlemler (append_rows() ile satır listesi) kullanın — Google’ın kota limiti proje başına 60 saniyede 300 istek.
Tabii bütün bu kurulum fazla geliyorsa, Thunderbit Google Sheets, Airtable, Notion, Excel, CSV ve JSON’a dışa aktarım yapar — kütüphane kurulumu yok, kimlik dosyası yok, API kotası yok.
Kodsuz Alternatif: Thunderbit ile Goodreads Kazıma
Herkes Python scripti bakımı yapmak istemeyebilir. Belki sen bir yayıncı olarak tek seferlik pazar analizi yapıyorsun ya da sadece bu yılın en çok satanlarından bir tablo çıkarmak isteyen bir kitap blog yazarıyısın. Thunderbit’i tam da bu senaryolar için geliştirdik.
Thunderbit ile Goodreads Nasıl Kazınır?
- üzerinden Thunderbit Chrome uzantısını kurun ve bir Goodreads liste, raf ya da arama sonuçları sayfasına gidin.
- Thunderbit yan panelinde "AI Suggest Fields" düğmesine tıklayın. Yapay zekâ sayfayı okur ve genellikle title, author, rating, cover image URL ve book link gibi sütunları önerir.
- "Scrape" düğmesine tıklayın — veri saniyeler içinde yapılandırılmış bir tabloya çekilir.
- Veriyi Google Sheets, Excel, Airtable, Notion, CSV veya JSON’a dışa aktarın.
Ayrıntılı kitap verisi (ISBN, açıklama, türler, sayfa sayısı) için Thunderbit’in alt sayfa kazıma özelliği her kitabın detay sayfasını ziyaret eder ve tabloyu otomatik olarak zenginleştirir — döngü yok, bekleme süresi yok, hata ayıklama yok.
Thunderbit ayrıca sayfalı listeleri de doğal olarak destekler. Sen sadece "Next"’e tıklamasını ya da kaydırmasını söylersin; o da tüm sayfalardan veriyi kod olmadan toplar.
Bedeli basittir: Python script sana tam kontrol ve ücretsiz kullanım sağlar (harcadığın zaman hariç), Thunderbit ise biraz esneklikten ödün vererek ciddi zaman kazancı ve bakım gerektirmeyen bir deneyim sunar. 300 kitaplık bir liste için Python scripti yaklaşık 25 dakikalık çalışma süresi ve ayrıca yazma/ayıklama süresi gerektirir. Thunderbit aynı veriyi iki tıkla yaklaşık 3 dakikada alır.
Goodreads’i Sorumlu Şekilde Kazımak: robots.txt, Hizmet Şartları ve Etik
Burada geçiştirilecek bir uyarı yerine net bir cevap vermek gerekir.
Goodreads’in robots.txt Dosyası Aslında Ne Diyor?
Goodreads’in canlı robots.txt dosyası şaşırtıcı derecede nettir. Kitap detay sayfaları (/book/show/), herkese açık listeler (/list/show/), herkese açık raflar (/shelf/show/) ve yazar sayfaları (/author/show/) engellenmez. Engellenenler: /api, /book/reviews/, /review/list, /review/show, /search ve başka bazı yollar. GPTBot ve CCBot (Common Crawl) tamamen Disallow: / ile engellenmiştir. bingbot için Crawl-delay: 5 direktifi vardır, ancak global bir gecikme kuralı yoktur.
Goodreads Hizmet Şartları Basitçe Ne Söylüyor?
Hizmet Şartları (son revizyon: 28 Nisan 2021), "her türlü veri madenciliği, robot veya benzer veri toplama ve çıkarma araçlarını" yasaklar. Bu geniş bir ifadedir ve ciddiye alınmalıdır — ancak mahkemeler yalnızca Hizmet Şartları ihlalinin tek başına cezai "izinsiz erişim" oluşturmadığı yönünde karar vermiştir. kararında, "hizmet şartı ihlallerini suç saymak, her web sitesini kendi ceza yargı alanına dönüştürme riskini taşır" denmiştir.
En İyi Uygulamalar
- İstekler arasında gecikme koyun: İstek başına 3-8 saniye (Goodreads’in robots.txt’si botlar için 5 saniye önerir)
- Günlük 5.000 isteğin altında kalın — tek IP için
- Yalnızca herkese açık sayfaları kazıyın — yalnızca giriş gerektiren verileri toplu olarak kazımaktan kaçının
- Ham yorum metnini ticari olarak yeniden dağıtmayın — yorumlar telif hakkı koruması altındaki yaratıcı eserlerdir
- Sadece ihtiyacınız olan veriyi saklayın ve bir veri saklama planı belirleyin
- Kişisel araştırma vs. ticari kullanım: Kamuya açık veriyi kişisel analiz veya akademik araştırma için kazımak genellikle kabul görür. Ticari yeniden dağıtımda yasal risk artar.
Thunderbit gibi bir araç kullanmak (kendi tarayıcı oturumunuz üzerinden kazıdığı için) etkileşimi görsel olarak normal gezintiyle aynı hale getirir; ancak araç ne olursa olsun aynı etik ilkeler geçerlidir. Web kazımanın hakkında daha derine inmek isterseniz bu konuyu ayrıca ele aldık.
İpuçları ve Sık Yapılan Hatalar
İpucu: Her zaman JSON-LD ile başlayın. Karmaşık CSS seçiciler yazmadan önce, ihtiyacınız olan verinin <script type="application/ld+json"> etiketinde olup olmadığını kontrol edin. Daha stabildir, ayrıştırması daha kolaydır ve Goodreads ön yüzünü güncellediğinde bozulma ihtimali daha düşüktür.
İpucu: İki aşamalı stratejiyi kullanın. Önce liste sayfalarından tüm kitap URL’lerini toplayın, sonra her detay sayfasını ziyaret edin. Böylece scraper’ın yarı yolda çökmesi durumunda kaldığınız yerden devam etmesi kolaylaşır ve URL listesini bir kontrol noktası olarak diske kaydedebilirsiniz.
Hata: Eksik alanları işlemeyi unutmak. Her kitap sayfasında ISBN, tür etiketi veya açıklama olmayabilir. Her zaman .get() için varsayılan değer kullanın ya da seçicileri if kontrolleriyle sarın. Tek bir NoneType hatası, 3 saatlik bir kazıma işlemini çökertir.
Hata: Fazla hızlı çalışmak. time.sleep(0.5) koyup hızla ilerlemek cazip gelebilir. Ama Goodreads yaklaşık 20-30 hızlı istekten sonra 403 döndürmeye başlar ve bir kez işaretlenirseniz saatlerce beklemeniz ya da IP değiştirmeniz gerekebilir. 4-5 saniyelik gecikme en ideal noktadır.
Hata: Eski eğitimlere güvenmek. Bir rehber Goodreads API’sinden bahsediyorsa ya da .field.value veya #bookTitle gibi class adları kullanıyorsa muhtemelen eskimiştir. Scraper’ınızı kurmadan önce seçicileri her zaman canlı sayfada doğrulayın.
Doğru scraping araçları ve çerçeveleri seçmek hakkında daha fazla bilgi için ve rehberlerimize göz atın.
Sonuç ve Öne Çıkan Noktalar
Python ile Goodreads kazımak tamamen yapılabilir — sadece mayınların nerede olduğunu bilmeniz gerekir. Kısaca:
- Goodreads API’si yok (Aralık 2020’den beri). Platformdan yapılandırılmış kitap verisi almanın ana yolu scraping.
- Boş sonuçlar neredeyse her zaman JS ile render edilen içerik, güncel olmayan seçiciler, eksik header’lar veya sayfalama kimlik doğrulama sorunlarından kaynaklanır — kodunun yanlış olmasından değil.
- JSON-LD, kitap metadata’sı için en iyi dostunuzdur. Kararlıdır, yapılandırılmıştır ve nadiren değişir.
- Sayfalama, birçok raf ve liste sayfasında 5. sayfadan sonra kimlik doğrulaması gerektirir.
_session_id2çerezini ekleyin. - Hız sınırı gerçektir. 3-8 saniyelik gecikmeler kullanın ve günlük 5.000 isteğin altında kalın.
- İki aşamalı strateji (önce URL’leri toplamak, sonra detay sayfalarını kazımak) daha güvenilir ve kaldığınız yerden devam edilebilir bir yöntemdir.
- Kod yazmayanlar için (veya öğleden sonrasını önemsiz yere kodla harcamak istemeyen herkes için) bunların hepsini — JS render, sayfalama, alt sayfa zenginleştirme ve dışa aktarma — yaklaşık iki tıkta halleder.
Sorumlu kazıyın, robots.txt’ye saygı gösterin ve kitap verinizin artık hiç olmazsa []’den fazlasını döndürmesini dileyin.
SSS
Goodreads API’si hâlâ kullanılabilir mi?
Hayır. Goodreads, kamuya açık API’sini Aralık 2020’de kullanım dışı bıraktı ve artık yeni geliştirici anahtarı vermiyor. 30 gün boyunca etkin olmayan mevcut anahtarlar da otomatik olarak devre dışı bırakıldı. Kitap verisine programatik erişim için şu anda seçenekler web scraping veya alternatif API’lerdir (Open Library ya da Google Books gibi).
Goodreads scraper’ım neden boş sonuç veriyor?
En yaygın neden JavaScript ile render edilen içeriktir. Goodreads, yorumları, puan dağılımlarını ve birçok detay bölümünü React/JavaScript ile yükler; basit bir requests.get() bunu göremez. Bu sayfalar için Selenium veya Playwright kullanın. Diğer nedenler arasında güncel olmayan CSS seçiciler (Goodreads HTML’yi değiştirmiş olabilir), eksik User-Agent header’ları (403 bloklarını tetikler) veya sayfalı raf sayfalarında kimlik doğrulamasız istekler bulunur.
Goodreads kazımak yasal mı?
Kamuya açık veriyi kişisel ya da araştırma amaçlı kazımak, mevcut yasal içtihatlara göre genellikle kabul edilebilir görülür (hiQ v. LinkedIn, Meta v. Bright Data). Ancak Goodreads’in Hizmet Şartları otomatik veri toplamayı yasaklar ve robots.txt dosyasını mutlaka incelemelisiniz. Telifli yorum metinlerini ticari olarak yeniden dağıtmaktan kaçının ve site kaynaklarını zorlamamak için istek hacminizi sınırlayın.
Goodreads’te birden fazla sayfa nasıl kazınır?
Raf ya da liste URL’sine ?page=N ekleyin ve sayfa numaraları üzerinde döngü kurun. Son sayfayı anlamak için boş sonuçları veya "next" bağlantısının yokluğunu kontrol edin. Önemli not: bazı raf sayfaları 5. sayfadan sonrası için sonuç döndürmek adına kimlik doğrulaması (_session_id2 çerezi) ister — bu olmadan sessizce 1. sayfa verisi tekrar eder.
Kod yazmadan Goodreads kazıyabilir miyim?
Evet. iki tıkla Goodreads kazımanıza izin veren bir Chrome uzantısıdır — yapay zekâ alanları önerir, sen "Scrape" dersin ve veriyi doğrudan Google Sheets, Excel, Airtable veya Notion’a dışa aktarırsın. JS ile render edilen içerik, sayfalama ve alt sayfa zenginleştirmeyi otomatik olarak yönetir; Python veya kod gerekmez.
Daha Fazla Bilgi