Scrapy, web scraping için en güçlü Python framework'lerinden biridir, ancak doğru proxy ayarı olmadan, tarayıcılarınız birkaç dakika içinde engellenebilir. Bu kılavuzda, Scrapy'de proxy entegrasyonu için tüm yöntemleri göstereceğim: en basit ayarlardan, otomatik hata işleme ile IP adreslerinin döngüsüne kadar.
Materyal, büyük e-ticaret siteleri ve korumalı web siteleri üzerinde yapılan gerçek veri çekme deneyimlerine dayanmaktadır. Projelerinizde hemen kullanabileceğiniz hazır kod örnekleri alacaksınız.
Neden Scrapy Proxy Olmadan Engelleniyor
Modern web siteleri, veri çekmeye karşı çok katmanlı koruma kullanmaktadır. User-Agent ve istekler arasındaki gecikmeleri ayarlasanız bile, IP adresiniz birkaç belirti ile otomasyonu ortaya çıkarır:
- İstek Sıklığı: bir IP, dakikada 100+ istek yapıyorsa - bu, bir botun açık bir işareti
- Davranış Kalıpları: rastgele geçişler olmadan sayfaların ardışık olarak gezilmesi
- JavaScript Eksikliği: Scrapy JS çalıştırmaz, bu kolayca tespit edilir
- Coğrafi Konum: ev ağı yerine veri merkezinden erişim
Sonuç - birkaç saat veya gün boyunca IP banı. Özellikle agresif koruma, pazar yerleri (Amazon, Wildberries, Ozon), sosyal medya ve Cloudflare ile korunan sitelerde kullanılır. Proxy'ler, istekleri birçok IP adresi arasında dağıtarak bu sorunu çözer.
Önemli: Proxy ile bile, oran limitlerine uymak gerekir. Önerilen hız: bir IP için saniyede 1-3 istek. Yüksek hızlı veri çekimi için 50+ proxy ile bir havuz kullanın ve döngü oluşturun.
Scrapy'de Proxy Temel Ayarları
En basit yöntem, proxy'yi doğrudan örümcek ayarlarında belirtmektir. Bu yöntem, test etme veya tek bir proxy sunucusuyla küçük veri hacimlerini çekmek için uygundur.
Yöntem 1: Request'te meta ile
import scrapy
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['https://example.com']
def start_requests(self):
proxy = 'http://username:password@proxy.example.com:8080'
for url in self.start_urls:
yield scrapy.Request(
url=url,
callback=self.parse,
meta={'proxy': proxy}
)
def parse(self, response):
# Veri çekme mantığınız
self.log(f'Scraped {response.url} via {response.meta["proxy"]}')
Proxy formatı, protokole ve kimlik doğrulama yöntemine bağlıdır:
http://proxy.example.com:8080— kimlik doğrulama olmadanhttp://user:pass@proxy.example.com:8080— kullanıcı/şifre ilesocks5://user:pass@proxy.example.com:1080— SOCKS5 proxy
Yöntem 2: settings.py'de Küresel Ayar
# settings.py
# Tüm istekler için HTTP proxy
HTTPPROXY_ENABLED = True
HTTPPROXY_AUTH_ENCODING = 'utf-8'
# Ortam değişkenleri ile ayarlama
HTTP_PROXY = 'http://username:password@proxy.example.com:8080'
HTTPS_PROXY = 'http://username:password@proxy.example.com:8080'
Bu yöntem hızlı testler için uygundur, ancak üretim için uygun değildir: IP döngüsü yoktur, proxy düştüğünde tüm tarayıcı durur, farklı siteler için farklı proxy'ler kullanmak mümkün değildir.
Özel Proxy Middleware Oluşturma
Üretim veri çekimi için, proxy havuzunu yönetecek, hataları işleyecek ve IP'leri otomatik olarak döndürecek özel bir middleware gerekir. İşte temel bir uygulama:
# middlewares.py
import random
from scrapy import signals
from scrapy.exceptions import NotConfigured
class RandomProxyMiddleware:
def __init__(self, proxy_list):
self.proxy_list = proxy_list
@classmethod
def from_crawler(cls, crawler):
# Ayarlardan proxy listesini yükle
proxy_list = crawler.settings.getlist('PROXY_LIST')
if not proxy_list:
raise NotConfigured('PROXY_LIST not configured')
return cls(proxy_list)
def process_request(self, request, spider):
# Havuzdan rastgele bir proxy seç
proxy = random.choice(self.proxy_list)
request.meta['proxy'] = proxy
spider.logger.info(f'Using proxy: {proxy}')
def process_exception(self, request, exception, spider):
# Hata durumunda başka bir proxy deneyin
proxy = random.choice(self.proxy_list)
request.meta['proxy'] = proxy
spider.logger.warning(
f'Proxy error, switching to: {proxy}'
)
return request
Şimdi settings.py dosyasında middleware kullanımını ayarlayalım:
# settings.py
# Proxy listesi (dosyadan veya API'den yüklenebilir)
PROXY_LIST = [
'http://user1:pass1@proxy1.example.com:8080',
'http://user2:pass2@proxy2.example.com:8080',
'http://user3:pass3@proxy3.example.com:8080',
# ... etkili döngü için 50+ proxy ekleyin
]
# Middleware'i bağlayın
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.RandomProxyMiddleware': 350,
'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 400,
}
# Hatalarda tekrar denemeler
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]
Proxy Döngüsü: Üç Çalışan Yöntem
Proxy'lerin rastgele seçimi (yukarıdaki örnekte olduğu gibi) en basit yöntemdir, ancak en etkili değildir. Farklı senaryolar için üç döngü stratejisini inceleyelim.
Yöntem 1: Round-robin (sıralı döngü)
Proxy'ler dairesel olarak seçilir. Yükü eşit dağıtmak için uygundur:
class RoundRobinProxyMiddleware:
def __init__(self, proxy_list):
self.proxy_list = proxy_list
self.current_index = 0
@classmethod
def from_crawler(cls, crawler):
proxy_list = crawler.settings.getlist('PROXY_LIST')
return cls(proxy_list)
def process_request(self, request, spider):
# Dairesel olarak bir sonraki proxy'yi al
proxy = self.proxy_list[self.current_index]
self.current_index = (self.current_index + 1) % len(self.proxy_list)
request.meta['proxy'] = proxy
Yöntem 2: Akıllı döngü ile blacklist
Sorunlu proxy'leri izleyip geçici olarak döngüden çıkarıyoruz:
import time
from collections import defaultdict
class SmartProxyMiddleware:
def __init__(self, proxy_list):
self.proxy_list = proxy_list
self.proxy_errors = defaultdict(int)
self.blacklist = set()
self.blacklist_timeout = 300 # 5 dakika
self.blacklist_time = {}
@classmethod
def from_crawler(cls, crawler):
proxy_list = crawler.settings.getlist('PROXY_LIST')
return cls(proxy_list)
def get_working_proxies(self):
# Blacklist'ten zaman aşımına uğramış proxy'leri çıkar
current_time = time.time()
expired = [
proxy for proxy, ban_time in self.blacklist_time.items()
if current_time - ban_time > self.blacklist_timeout
]
for proxy in expired:
self.blacklist.discard(proxy)
self.proxy_errors[proxy] = 0
# Çalışan proxy'leri döndür
return [p for p in self.proxy_list if p not in self.blacklist]
def process_request(self, request, spider):
working_proxies = self.get_working_proxies()
if not working_proxies:
spider.logger.error('Tüm proxy'ler blacklist'te!')
return
proxy = random.choice(working_proxies)
request.meta['proxy'] = proxy
def process_response(self, request, response, spider):
# Eğer bir engelleme aldıysak - blacklist'e ekle
if response.status in [403, 429, 503]:
proxy = request.meta.get('proxy')
self.proxy_errors[proxy] += 1
if self.proxy_errors[proxy] >= 3:
self.blacklist.add(proxy)
self.blacklist_time[proxy] = time.time()
spider.logger.warning(
f'Proxy {proxy} {self.blacklist_timeout}s süreyle blacklist'e alındı'
)
return response
Yöntem 3: Sağlayıcının API'si ile döngü
Birçok proxy sağlayıcısı (dahil olmak üzere rezidans proxy'leri) döngüsel uç nokta sağlar - her istekte otomatik olarak IP'yi değiştiren tek bir URL:
# settings.py
# Otomatik döngü ile tek uç nokta
ROTATING_PROXY = 'http://username:password@rotating.proxy.com:8080'
# Basit middleware
class RotatingProxyMiddleware:
def __init__(self, proxy):
self.proxy = proxy
@classmethod
def from_crawler(cls, crawler):
proxy = crawler.settings.get('ROTATING_PROXY')
return cls(proxy)
def process_request(self, request, spider):
# Tek URL, ancak her istek yeni bir IP ile
request.meta['proxy'] = self.proxy
Bu, üretim için en uygun yöntemdir: proxy havuzunu yönetmek zorunda kalmazsınız, sağlayıcı IP kalitesini izler ve sorunlu olanları değiştirir. Özellikle rezidans proxy'leri ile etkili çalışır, burada IP havuzu milyonlarca adrese ulaşabilir.
Kimlik Doğrulama: Kullanıcı/Şifre vs IP Beyaz Listesi
Proxy sağlayıcıları iki kimlik doğrulama yöntemi sunar. Seçim, bağlantı hızını ve ayarların kolaylığını etkiler.
Kullanıcı:Şifre Kimlik Doğrulaması
Kullanıcı adı ve şifre, proxy URL'sinde iletilir. Scrapy, bunları otomatik olarak Proxy-Authorization HTTP başlığına dönüştürür:
proxy = 'http://username:password@proxy.example.com:8080'
request.meta['proxy'] = proxy
# Scrapy otomatik olarak başlığı ekleyecek:
# Proxy-Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Artılar: her IP'den çalışır, kodda proxy'leri kolayca değiştirmek mümkündür.
Eksiler: her istekte küçük bir gecikme (~50-100ms), açık metin olarak kodda kimlik bilgileri.
IP Beyaz Listesi Kimlik Doğrulaması
Sunucunuzun IP'sini sağlayıcının beyaz listesine ekliyorsunuz, kimlik doğrulama gerekmez:
proxy = 'http://proxy.example.com:8080' # kullanıcı/şifre olmadan
request.meta['proxy'] = proxy
Artılar: 50-100ms daha hızlı, daha güvenli (kodda kimlik bilgileri yok).
Eksiler: sadece belirli IP'lerden çalışır, sunucu değiştiğinde beyaz listeyi güncellemek gerekir.
Üretim için öneri:
Özel sunuculardan (AWS, Google Cloud, Hetzner) veri çekmek için IP beyaz listesini kullanın. Geliştirme ve test için yerel makineden - kullanıcı:şifre kimlik doğrulamasını kullanın.
Hata İşleme ve Otomatik IP Değiştirme
Kaliteli proxy'lerle bile hatalar olacaktır: zaman aşımı, bağlantı reddi, engellemeler. Hataların doğru bir şekilde işlenmesi, tarayıcının stabil çalışması için kritik öneme sahiptir.
HTTP Statülerinin İşlenmesi
class ProxyMiddleware:
def process_response(self, request, response, spider):
# Proxy'yi değiştirmek ve tekrar denemek için gereken kodlar
ban_codes = [403, 407, 429, 503]
if response.status in ban_codes:
proxy = request.meta.get('proxy')
spider.logger.warning(
f'{proxy} ile {response.status} alındı, tekrar deniyoruz...'
)
# Yeni proxy ile tekrar denemek için işaretle
request.meta['dont_retry'] = False
request.meta['proxy'] = self.get_new_proxy()
return request
return response
Ağ Hatalarının İşlenmesi
from twisted.internet.error import TimeoutError, ConnectionRefusedError
from scrapy.exceptions import IgnoreRequest
class ProxyMiddleware:
def process_exception(self, request, exception, spider):
# Proxy bağlantı hataları
proxy_errors = (
TimeoutError,
ConnectionRefusedError,
ConnectionLost,
)
if isinstance(exception, proxy_errors):
proxy = request.meta.get('proxy')
spider.logger.error(
f'Proxy {proxy} bağlantısı başarısız: {exception}'
)
# Proxy'yi değiştir ve tekrar dene
request.meta['proxy'] = self.get_new_proxy()
return request
# Diğer hatalar için standart işleme kullan
return None
İçerik Üzerinden Engellemelerin Tespiti
Bazı siteler HTTP 200 döndürür, ancak CAPTCHA veya engelleme sayfası gösterir:
class ProxyMiddleware:
def process_response(self, request, response, spider):
# İçerikteki engelleme belirtileri
ban_indicators = [
'captcha',
'access denied',
'blocked',
'unusual traffic',
'robot check',
]
body_text = response.text.lower()
if any(indicator in body_text for indicator in ban_indicators):
spider.logger.warning(
f'{request.meta.get("proxy")} üzerinden engelleme sayfası tespit edildi'
)
# Proxy'yi değiştir ve tekrar dene
request.meta['proxy'] = self.get_new_proxy()
return request
return response
Scrapy için Hangi Proxy Türünü Seçmelisiniz
Proxy türünün seçimi, hedef siteye, bütçeye ve gerekli veri çekme hızına bağlıdır. İşte ana seçeneklerin karşılaştırması:
| Proxy Türü | Hız | Maliyet | Ne Zaman Kullanmalı |
|---|---|---|---|
| Veri Merkezi Proxy | Yüksek (50-200ms) | Düşük ($1-3/IP) | Koruması olmayan basit siteler, API'ler, iç araçlar |
| Rezidans Proxy | Orta (300-800ms) | Orta ($5-15/GB) | E-ticaret, sosyal medya, Cloudflare ile korunan siteler, coğrafi hedefleme |
| Mobil Proxy | Düşük (500-1500ms) | Yüksek ($50-150/IP) | Mobil uygulamalar, Instagram, TikTok, maksimum koruma |
Seçim Önerileri
Pazar yerlerini çekmek için (Amazon, Wildberries, Ozon, AliExpress) - sadece rezidans proxy'leri kullanın. Bu siteler veri merkezlerini agresif bir şekilde engeller. Döngü ve coğrafi hedefleme gereklidir (örneğin, Wildberries için Rus IP'leri).
Haber siteleri, bloglar, forumlar için - veri merkezi proxy'leri uygundur. Koruma minimumdur, hız ve düşük trafik maliyeti önemlidir.
Cloudflare ile korunan siteleri çekmek için - rezidans proxy'leri zorunludur. Cloudflare, veri merkezlerini neredeyse anında tespit eder. JS zorluklarını aşmak için Scrapy'ye cloudscraper kütüphanesini ekleyin.
Google Arama, SEO araçlarını çekmek için - coğrafi hedefleme ile rezidans proxy'leri kullanın. Google, farklı ülkeler ve şehirler için farklı sonuçlar gösterir.
Tavsiye: Test için 10 rezidans proxy havuzuyla başlayın. Engellemeler alıyorsanız - havuzu 50-100 IP'ye çıkarın. Yüksek hızlı veri çekimi (dakikada 1000+ istek) için 10,000+ IP havuzuyla döngüsel uç nokta kullanın.
Gelişmiş Teknikler: Oturumlar ve Sticky IP
Bazı siteleri çekerken, oturum boyunca tek bir IP'yi korumak gerekir (kimlik doğrulama, alışveriş sepeti, çok aşamalı formlar). İşte Scrapy'de sticky oturumları nasıl uygulayacağınız:
Tek bir alan adı için Sticky IP
from urllib.parse import urlparse
class StickyProxyMiddleware:
def __init__(self, proxy_list):
self.proxy_list = proxy_list
# Sözlük: alan adı -> proxy
self.domain_proxy_map = {}
@classmethod
def from_crawler(cls, crawler):
proxy_list = crawler.settings.getlist('PROXY_LIST')
return cls(proxy_list)
def process_request(self, request, spider):
# URL'den alan adını çıkar
domain = urlparse(request.url).netloc
# Bu alan adı için zaten bir proxy varsa - onu kullan
if domain in self.domain_proxy_map:
proxy = self.domain_proxy_map[domain]
else:
# Aksi takdirde yeni bir proxy seç ve kaydet
proxy = random.choice(self.proxy_list)
self.domain_proxy_map[domain] = proxy
spider.logger.info(f'{domain} için {proxy} atandı')
request.meta['proxy'] = proxy
Oturum Zaman Aşımı ile Sticky IP
Daha gelişmiş bir seçenek: proxy, belirli bir süre (örneğin, 10 dakika) için alan adına bağlanır, ardından değişir:
import time
from urllib.parse import urlparse
class SessionProxyMiddleware:
def __init__(self, proxy_list, session_timeout=600):
self.proxy_list = proxy_list
self.session_timeout = session_timeout # 10 dakika
# Sözlük: alan adı -> (proxy, oluşturma zamanı)
self.sessions = {}
@classmethod
def from_crawler(cls, crawler):
proxy_list = crawler.settings.getlist('PROXY_LIST')
timeout = crawler.settings.getint('PROXY_SESSION_TIMEOUT', 600)
return cls(proxy_list, timeout)
def get_proxy_for_domain(self, domain):
current_time = time.time()
# Aktif bir oturum var mı kontrol et
if domain in self.sessions:
proxy, created_at = self.sessions[domain]
# Eğer oturum süresi dolmadıysa - aynı proxy'yi kullan
if current_time - created_at < self.session_timeout:
return proxy
# Yeni bir oturum oluştur ve yeni bir proxy ile
new_proxy = random.choice(self.proxy_list)
self.sessions[domain] = (new_proxy, current_time)
return new_proxy
def process_request(self, request, spider):
domain = urlparse(request.url).netloc
proxy = self.get_proxy_for_domain(domain)
request.meta['proxy'] = proxy
Cookie Middleware ile Entegrasyon
Tam oturumlar için proxy ve çerezleri senkronize etmek gerekir. Scrapy, çerezleri her alan adı için ayrı saklar, ancak proxy değiştiğinde çerezleri temizlemek gerekir:
# settings.py
# Çerez middleware'ini etkinleştir
COOKIES_ENABLED = True
COOKIES_DEBUG = False
# Proxy ve çerezlerin senkronizasyonu için middleware
class ProxyCookieMiddleware:
def process_request(self, request, spider):
# Mevcut proxy'yi al
current_proxy = request.meta.get('proxy')
# Eğer proxy değiştiyse - çerezleri temizle
previous_proxy = request.meta.get('previous_proxy')
if previous_proxy and previous_proxy != current_proxy:
# Bu alan adı için çerezleri temizle
jar = spider.crawler.engine.downloader.middleware.middlewares[0].jars
domain = urlparse(request.url).netloc
if domain in jar:
jar[domain].clear()
spider.logger.info(f'{domain} için çerezler temizlendi')
request.meta['previous_proxy'] = current_proxy
Sonuç
Scrapy'de proxy ayarlarının doğru bir şekilde yapılması, engellenmeden stabil veri çekiminin temelidir. Temel entegrasyondan, gelişmiş döngü ve oturum yönetimi tekniklerine kadar tüm ana noktaları inceledik.
Ana çıkarımlar:
- Üretim için akıllı döngü ve sorunlu IP'lerin blacklist'ini kullanan özel middleware kullanın
- Tüm hata türlerini işleyin: HTTP statüleri, ağ istisnaları, içerik üzerinden engellemeler
- Görev için uygun proxy türünü seçin: basit siteler için veri merkezleri, korumalı siteler için rezidans
- Kimlik doğrulama gerektiren siteler için proxy'yi alan adına bağlayarak sticky oturumlar kullanın
- 10-50 proxy ile başlayın, yük arttıkça ölçeklendirin
Koruma altındaki siteleri (pazar yerleri, sosyal medya, Cloudflare ile korunan siteler) çekmeyi planlıyorsanız, rezidans proxy'leri kullanmanızı öneririm - maksimum anonimlik ve minimum engel riski sağlarlar. Yüksek hızlı veri çekimi için döngüsel uç nokta ve 10,000 IP'den oluşan bir havuz seçin.
Bu makalede yer alan tüm kod örnekleri Scrapy 2.x üzerinde test edilmiştir ve üretimde kullanılmaya hazırdır. Kendi ihtiyaçlarınıza göre uyarlayın ve projenizin büyümesiyle ölçeklendirin.