Engellenmeden web scraping yapmak: kod örnekleriyle pratik rehber
Scraping sırasında engellenmek genellikle üç şeyden birini anlatır: çok fazla isteği çok hızlı gönderdiniz, istemciniz bir tarayıcıya ya da bilinen bir bota hiç benzemedi ya da IP adresiniz sitenin daha az güvendiği bir listede. Üçünü de birkaç alışkanlıkla azaltabilirsiniz: bloğu erken fark edin, site geri ittiğinde yavaşlayın, tutarlı ve dürüst bir kimlik tutun ve proxy'leri yalnızca uygun olduğu yerlerde kullanın. Bu rehber her birini çalışan kodla anlatır. Yalnızca taramanıza izin verilen siteler için kullanın.
Bir bloğu tanıyın
Bir blok her zaman bir hata sayfası gibi görünmez. Şunlara dikkat edin:
- 403 Forbidden, genellikle sitenin istemcinizi reddettiği anlamına gelir. Tekrar denemeden önce orada olmanıza izin verilip verilmediğini kontrol edin.
- 429 Too Many Requests, çok fazla istek gönderdiğiniz anlamına gelir. Genellikle bir
Retry-Afterbaşlığıyla gelir. - 503 Service Unavailable, sitenin aşırı yüklendiği anlamına gelebilir. Geri çekilin.
- Neredeyse hiç içeriği olmayan bir 200 yanıtı, bir giriş ya da challenge sayfasına yönlendirme ya da beklediğiniz veriyi içermeyen bir sayfa.
Her başarısızlık için durum kodunu ve gövdenin kısa bir örneğini loglayın. Loglarınızda göremediğiniz bir blok, düzeltemeyeceğiniz bir bloktur.
Yeniden denemelerle geri çekilin
Site "yavaşla" dediğinde, kodunuz bir nebze değil, belirgin şekilde yavaşlamalıdır. Deneme sayısında bir sınır olan üssel geri çekilme (exponential backoff) standart yaklaşımdır. Site gönderdiğinde Retry-After değerine de uyar:
import time
import requests
session = requests.Session()
session.headers.update({
"User-Agent": "ExampleBot/1.0 (+https://example.com/bot)",
"Accept-Language": "en-US,en;q=0.9",
})
def polite_get(url: str, max_attempts: int = 4) -> requests.Response:
delay = 2.0
resp = None
for _ in range(max_attempts):
resp = session.get(url, timeout=30)
if resp.status_code not in (429, 503):
return resp
retry_after = resp.headers.get("Retry-After", "")
wait = float(retry_after) if retry_after.isdigit() else delay
time.sleep(wait)
delay *= 2
return resp # hâlâ engelli: son yanıtı çağıran taraf ele alsın
resp = polite_get("https://example.com/products?page=1")
print(resp.status_code, len(resp.text))
Fonksiyonun yapmadıklarına dikkat edin. 403'ü yeniden denemez ve bir engeli aşmak için kimlikler arasında dönmez. 403, çözülecek bir bulmaca değil, durma kararıdır.
Bir bloğu adım adım teşhis edin
Bir çalıştırma başarısız olmaya başladığında, başka bir şeyi değiştirmeden önce şu kontrolleri yapın:
- İlk başarısız URL'ye ve durum koduna bakın. Bir blok genellikle rastgele değil, belirli bir noktada başlar.
- İlk başarısızlıktan önceki dakikalardaki istek hızını karşılaştırın. Bir patlama en yaygın nedendir.
- Hatanın tek bir yola, tek bir bölüme ya da günün tek bir saatine bağlı olup olmadığını kontrol edin.
- Aynı URL'yi normal bir tarayıcıda elle bir kez açın. Tarayıcı yüklüyorsa fark istek örüntünüzdedir. Tarayıcı da engelleniyorsa, site şimdilik size sayfayı sunmayı bırakmıştır.
- Ancak bundan sonra kodu değiştirin. Önce yavaşlayın, ikinci olarak geri çekilmeyi ekleyin, en son kimliğinizi gözden geçirin.
Birçok şeyi aynı anda değiştirmek, hangisinin yardımcı olduğunu bilmeyi imkânsız kılar.
Tutarlı ve dürüst bir kimlik koruyun
Scraper'lar, kimlikleri istekler arasında değiştiğinde ya da trafiğin geri kalanıyla eşleşmediğinde engellenir. Tek bir oturum ve net bir User-Agent en basit kararlı kimliktir. Botunuza bir ad verin, bir iletişim bağlantısı ekleyin ve yalnızca normal bir istemcinin göndereceği başlıkları gönderin. Pek çok farklı ziyaretçiye benzemek için User-Agent'ları değiştirmek bir kaçınma biçimidir ve bloğu azaltmak yerine artırma eğilimindedir.
Bir site tarayıcılara ve düz HTTP istemcilerine farklı içerik sunuyorsa, o sayfalar için gerçek bir tarayıcı kullanın ve aynı hız sınırını koruyun. Bu meşru bir yoldur ve istek örüntüsünü makul tutar.
Proxy'leri dikkatle kullanın
Proxy, bir isteğin geldiği adresi değiştirir. Meşru ihtiyaçlar için bir araçtır; örneğin test etmeye izniniz olan bir sitede belirli bir bölgeden test yapmak ya da yükü kendi altyapınız arasında dağıtmak. Sitenin size koyduğu bir limiti aşmanın yolu değildir. Proxy formatı, proxy görev tiplerinin kullandığı formattır:
proxies = {
"http": "http://user:pass@proxy.example.net:8000",
"https": "http://user:pass@proxy.example.net:8000",
}
resp = session.get("https://example.com/products", proxies=proxies, timeout=30)
Proxy tipini seçmek ayrı bir sorudur ve residential ve datacenter proxy karşılaştırmasında ve scraping için rotating proxy rehberinde ele alınır.
Blok bir captcha'ya dönüştüğünde
Bazı siteler bir hız sorununa sert bir engelleme yerine bir captcha ile yanıt verir. Mantıklı sıra şudur: dur, yavaşla, kuralları yeniden kontrol et ve ancak bundan sonra bir çözümü değerlendir. PopularCaptcha widget'ları için Scarnox'un canlı API'si, form alanında göndermeniz gereken h-captcha-response token'ını döndürür. Diğer captcha tipleri için canlı listeyi kontrol edin; reCAPTCHA, Cloudflare Turnstile, GeeTest, AWS WAF ve Arkose desteği yakında geliyor. Ayrıntılı rehber web scraping captcha'ları sayfasındadır.
Bir sitede bloklar görmeye başladıysanız, sebep neredeyse her zaman istek örüntüsüdür. Scraping'de captcha'lardan kaçınmak bunları önleyen alışkanlıkları anlatır.
Sorumlu scraping
- Başlamadan önce
robots.txtdosyasını ve sitenin kullanım koşullarını kontrol edin; belirtilen yolları ve gecikmeleri izleyin. - Bir 403 ya da açık bir "scrape etmeyin" mesajı aldığınızda durun. Denemeye devam etmeyin.
- İstek hızınızı, normal bir ziyaretçinin trafiğinizi fark etmeyeceği kadar düşük tutun.
- İhtiyacınız olmayan kişisel veriyi tutmayın ve topladığınız her veriyi size uygulanan gizlilik kurallarına göre işleyin.
- Bir sitenin kaldırabileceğinden çok daha fazla hacme ihtiyacınız varsa, sahibinden bir veri akışı ya da API isteyin.