---
title: "Scrapy Nedir ve Proxy ile Nasıl Kullanılır?"
description: "Scrapy'de proxy, isteğin meta alanıyla ya da bir middleware ile tanımlanır. Kurulumu, kimlik doğrulamayı, AutoThrottle ayarını ve rotasyonu kodla anlatıyoruz."
url: https://proxynet.io/tr/blog/scrapy-proxy
date: 2026-09-19
author: "Acar Diveroli"
category: "Web Scraping, Nasıl Yapılır"
lang: tr
---

# Scrapy Nedir ve Proxy ile Nasıl Kullanılır?

Requests ve BeautifulSoup ile yazdığınız betik yüz sayfada sorunsuz çalışır. On bin sayfaya çıktığınızda işin kendisi değişir: hangi adresin tarandığını, hangisinin hata verip yeniden deneneceğini, aynı siteye aynı anda kaç istek gittiğini ve verinin nereye yazılacağını sizin yönetmeniz gerekir. Scrapy bu işlerin hepsini hazır getiren bir Python çatısıdır. Proxy ayarı da bu yapının içinde, isteğin geçtiği katmanlardan birinde durur.

Bu yazıda önce Scrapy'nin ne olduğunu ve nasıl kurulduğunu kısaca gösteriyoruz. Ardından asıl konuya geçiyoruz: proxy'nin üç tanımlama yolu, kimlik doğrulama, tek uç noktalı rotating gateway ile kendi proxy listeniz arasındaki fark, `DOWNLOAD_DELAY` ve AutoThrottle ile hız ayarı, `429` ve `503` yanıtlarında Scrapy'nin davranışı. Bütün örnekleri Scrapy 2.19 ve Python 3.13 ile, alıştırma için kurulmuş [toscrape.com](https://toscrape.com/) siteleri ve yerel bir test proxy'si üzerinde çalıştırdık.

> **Not: Kısa cevap**
>
> Scrapy; istek kuyruğu, eşzamanlılık, yeniden deneme ve dışa aktarma işlerini üstlenen açık kaynaklı bir Python tarama çatısıdır. Proxy, isteğin `meta["proxy"]` alanına `http://user:pass@pr.proxynet.io:8000` biçiminde bir adres yazılarak tanımlanır; bunu istek başına elle, `https_proxy` ortam değişkeniyle ya da birkaç satırlık bir downloader middleware ile bütün isteklere yapabilirsiniz. Rotating gateway kullanıyorsanız rotasyon için ek bir middleware gerekmez. Hızı `DOWNLOAD_DELAY`, `CONCURRENT_REQUESTS_PER_DOMAIN` ve AutoThrottle belirler.

## Scrapy nedir, ne işe yarar?

Scrapy, web sitelerini taramak ve sayfalardan yapılandırılmış veri çıkarmak için yazılmış bir Python çatısıdır. Bir kütüphaneden farkı şudur: Requests size tek bir istek gönderme aracı verir, döngüyü siz kurarsınız. Scrapy'de ise döngü çatının kendisidir. Siz yalnızca hangi adresten başlanacağını ve gelen sayfadan neyin alınacağını anlatan bir sınıf yazarsınız; bu sınıfa **spider** denir.

Spider'ın dışında kalan her şey hazırdır: istekleri sıraya koyan zamanlayıcı, aynı adresi ikinci kez istemeyen yineleme filtresi, asenkron indirici, yeniden deneme, robots.txt denetimi, hız sınırlama ve JSON ya da CSV'ye dışa aktarma. Seçiciler için CSS ve XPath birlikte kullanılır; ikisinin farkını [CSS Selector ve XPath](/tr/blog/css-selector-vs-xpath) yazımızda anlattık. Python ve JavaScript tarafındaki araçların genel haritası ise [Web Kazıma: JavaScript mi Python mu?](/tr/blog/web-scraping-javascript-vs-python) yazımızda.

## Scrapy kurulumu nasıl yapılır?

Scrapy'yi sistem Python'una değil, projeye özel bir sanal ortama kurun. Windows'ta komutlar şöyle:

```bash
python -m venv venv
venv\Scripts\activate
pip install scrapy
scrapy startproject kitaplik
cd kitaplik
scrapy genspider kitaplar books.toscrape.com
```

macOS ve Linux'ta ikinci satır `source venv/bin/activate` olur. `startproject` komutu `settings.py`, `middlewares.py` ve `spiders/` klasörünü içeren bir iskelet üretir. Güncel şablonun `settings.py` dosyası üç ayarı hazır getirir: `ROBOTSTXT_OBEY = True`, `CONCURRENT_REQUESTS_PER_DOMAIN = 1` ve `DOWNLOAD_DELAY = 1`. Yani yeni bir proje varsayılan olarak robots.txt'ye uyar ve aynı siteye saniyede yaklaşık bir istek gönderir. Bu satırları silmeyin, ayarlarınızı bunların üzerine kurun.

`genspider` ile oluşan dosyayı aşağıdaki gibi doldurun. Spider, kitap kartlarını okur ve "sonraki sayfa" bağlantısını izler:

```python
import scrapy

class KitaplarSpider(scrapy.Spider):
    name = "kitaplar"
    allowed_domains = ["books.toscrape.com"]
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for kart in response.css("article.product_pod"):
            yield {
                "baslik": kart.css("h3 a::attr(title)").get(),
                "fiyat": kart.css("p.price_color::text").get(),
                "adres": response.urljoin(kart.css("h3 a::attr(href)").get()),
            }

        sonraki = response.css("li.next a::attr(href)").get()
        if sonraki:
            yield response.follow(sonraki, callback=self.parse)
```

`scrapy crawl kitaplar -O kitaplar.json` komutu spider'ı çalıştırır ve sonucu dosyaya yazar. Denemede ilk iki sayfadan 40 kayıt geldi. Türkçe karakterlerin ve para birimi simgelerinin dosyada bozulmaması için şablondaki `FEED_EXPORT_ENCODING = "utf-8"` satırını koruyun.

## Scrapy bir isteği nasıl işler?

Proxy ayarının nereye yazılacağını anlamak için isteğin izlediği yolu bilmek gerekir:

1. Spider bir `Request` nesnesi üretir (`start_urls` ya da `response.follow`).
2. Motor isteği zamanlayıcıya verir; yineleme filtresi daha önce istenmiş adresi eler.
3. Sırası gelen istek **downloader middleware** zincirinden geçer. Her middleware bir sıra numarası taşır ve istek küçük numaradan büyüğe doğru ilerler: robots.txt denetimi 100, yeniden deneme 550, `HttpProxyMiddleware` 750.
4. İndirici isteği ağa gönderir. İstekte `meta["proxy"]` varsa bağlantı hedefe değil proxy'ye açılır; HTTPS adreslerde proxy üzerinden bir `CONNECT` tüneli kurulur.
5. Yanıt aynı zincirden ters sırayla geri döner ve spider'ın geri çağrı fonksiyonuna (`parse`) ulaşır.
6. Fonksiyonun ürettiği kayıtlar item pipeline'a, yeni istekler yeniden zamanlayıcıya gider.

Buradan çıkan sonuç: proxy, spider'ın ayrıştırma koduna değil üçüncü adıma aittir. `meta["proxy"]` alanını kim doldurursa doldursun, işi yapan `HttpProxyMiddleware`'dir. Bu middleware varsayılan olarak açıktır; [Scrapy belgelerindeki HttpProxyMiddleware bölümü](https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#module-scrapy.downloadermiddlewares.httpproxy) davranışını tanımlar.

## Scrapy'de proxy nasıl tanımlanır?

Üç yol var ve üçü de sonunda aynı `meta["proxy"]` alanını doldurur.

| Yöntem | Nereye yazılır | Kapsam | Ne zaman uygun |
|---|---|---|---|
| `meta["proxy"]` | Spider'da, her `Request` içinde | Yalnızca o istek | Birkaç isteği farklı bir çıkıştan göndermek |
| Ortam değişkeni | Kabukta `http_proxy` / `https_proxy` | Bütün istekler, robots.txt dahil | Hızlı deneme, kod değiştirmeden çalıştırma |
| Downloader middleware | `middlewares.py` + `settings.py` | Bütün istekler, robots.txt dahil | Kalıcı proje kurulumu, rotasyon |

### İstek başına: meta alanı

```python
import scrapy

PROXY = "http://user:pass@pr.proxynet.io:8000"

class KitaplarMetaSpider(scrapy.Spider):
    name = "kitaplar_meta"
    allowed_domains = ["books.toscrape.com"]

    async def start(self):
        yield scrapy.Request("https://books.toscrape.com/", meta={"proxy": PROXY})

    def parse(self, response):
        for kart in response.css("article.product_pod"):
            yield {"baslik": kart.css("h3 a::attr(title)").get()}

        sonraki = response.css("li.next a::attr(href)").get()
        if sonraki:
            # meta yeni isteğe kendiliğinden geçmez, elle taşınır
            yield response.follow(sonraki, callback=self.parse, meta={"proxy": PROXY})
```

Bu yöntemin gözden kaçan iki sonucu var ve ikisini de testte gördük. Birincisi, `meta` bir sonraki isteğe kendiliğinden aktarılmaz. `response.follow` çağrısına `meta` eklemediğimiz denemede ilk sayfa proxy'den, ikinci sayfa doğrudan kendi IP adresimizden gitti. İkincisi, Scrapy'nin robots.txt isteği sizin yazdığınız bir istek olmadığı için `meta` taşımaz ve o da proxy'siz çıkar. Bütün trafiğin aynı çıkıştan gitmesini istiyorsanız aşağıdaki iki yoldan birini kullanın.

### Ortam değişkeni

`HttpProxyMiddleware`, Python'un standart kütüphanesi gibi `http_proxy`, `https_proxy` ve `no_proxy` değişkenlerini okur. Kodda hiçbir şey değiştirmeden:

```bash
export https_proxy="http://user:pass@pr.proxynet.io:8000"
export http_proxy="$https_proxy"
scrapy crawl kitaplar
```

Bir istekte `meta["proxy"]` de varsa o değer ortam değişkeninin önüne geçer ve `no_proxy` listesini yok sayar. Değişkenlerin Windows, macOS ve Linux'ta nasıl tanımlandığını ve büyük-küçük harf farkını [wget ile Proxy Kullanımı](/tr/blog/wget-proxy) yazımızda anlattık; burada tekrar etmiyoruz.

### Bütün proje için: küçük bir middleware

Kalıcı kurulumda adresi koda gömmek yerine ortamdan okuyan birkaç satırlık bir middleware yeterlidir:

```python
# kitaplik/middlewares.py
from scrapy.exceptions import NotConfigured

class TekProxyMiddleware:
    """Ayarlardaki tek proxy adresini bütün isteklere uygular."""

    def __init__(self, adres):
        self.adres = adres

    @classmethod
    def from_crawler(cls, crawler):
        adres = crawler.settings.get("PROXY_ADRESI")
        if not adres:
            raise NotConfigured
        return cls(adres)

    def process_request(self, request):
        request.meta.setdefault("proxy", self.adres)
        return None
```

```python
# kitaplik/settings.py
import os

PROXY_ADRESI = os.environ.get("PROXY_ADRESI")
DOWNLOADER_MIDDLEWARES = {
    "kitaplik.middlewares.TekProxyMiddleware": 610,
}
```

`setdefault` sayesinde bir isteğe elle yazdığınız `meta["proxy"]` korunur. `PROXY_ADRESI` tanımlı değilse middleware kendini kapatır ve spider proxy'siz çalışır. Sıra numarasının 750'den küçük olması gerekir; böylece adresi önce siz yazarsınız, kimlik bilgisini `HttpProxyMiddleware` ayrıştırır. Eski sürümlerle yazılmış örneklerde imzayı `process_request(self, request, spider)` biçiminde görürsünüz; güncel belgede `spider` parametresi yoktur ve örnek bu haliyle Scrapy 2.19'da çalışır.

## Kimlik doğrulama nasıl çalışır?

Adresin içindeki `user:pass` kısmını `HttpProxyMiddleware` ayırır, Base64 ile kodlar ve isteğe `Proxy-Authorization: Basic …` başlığı olarak ekler. `meta["proxy"]` alanında geriye kimlik bilgisi içermeyen adres kalır; günlükte parolanızı görmezsiniz. Parolada `@`, `:` ya da `/` varsa yüzde kodlamasıyla yazın (`@` yerine `%40`); middleware değeri göndermeden önce çözer.

Yanlış parolayla yaptığımız denemede Scrapy şu satırı yazdı:

```text
TunnelError: Could not open CONNECT tunnel with proxy 127.0.0.1:8120 [{'status': 407, 'reason': b'Proxy Authentication Required'}]
```

HTTPS adreslerde `407` bir yanıt olarak değil, tünel kurulamadığı için bir istisna olarak gelir. Dikkat edilecek nokta: yeniden deneme middleware'i bu istisnayı geçici hata sayar ve aynı isteği iki kez daha dener. Yanlış parola tekrarla düzelmez; günlükte `TunnelError` ve `407` görüyorsanız taramayı durdurup kimlik bilgisini düzeltin. Kullanıcı adı ve parola yerine IP whitelist kullanıyorsanız adres `http://pr.proxynet.io:8000` biçiminde, kimlik bilgisi olmadan yazılır. İki yöntemin farkı [Proxy Kimlik Doğrulama: User:Pass ve IP Whitelist](/tr/blog/proxy-authentication-methods) yazımızda.

## Rotasyon için middleware gerekir mi?

Bu sorunun cevabı elinizdeki proxy'nin türüne bağlıdır.

**Rotating gateway kullanıyorsanız gerekmez.** [Rotating proxy](/tr/rotating-proxy) hizmetinde tek bir uç noktaya bağlanırsınız ve çıkış IP'sini ağ geçidi değiştirir. Scrapy tarafında yukarıdaki `TekProxyMiddleware` ya da tek bir ortam değişkeni yeter; liste tutmak, sıradaki adresi seçmek ve bozuk adresi ayıklamak sizin işiniz olmaz. [Residential Proxy](https://proxynet.io/tr/residential-proxy) ile yapılan taramalarda tipik kurulum budur. Rotasyon modlarının nasıl çalıştığını [IP Rotasyonu Nedir ve Nasıl Çalışır?](/tr/blog/ip-rotation-explained) yazımızda anlattık.

**Elinizde sabit adreslerden oluşan bir liste varsa** (örneğin birkaç [Datacenter Proxy](https://proxynet.io/tr/datacenter-proxy) ya da [ISP Proxy](https://proxynet.io/tr/static-isp-residential-proxy) adresi) dağıtımı bir middleware yapar. Aşağıdaki örnek adresleri sırayla kullanır, art arda hata veren adresi bir süre dinlendirir ve sayıları Scrapy istatistiklerine yazar:

```python
# kitaplik/middlewares.py
import time
from urllib.parse import urlsplit

from scrapy.exceptions import IgnoreRequest, NotConfigured

def anahtar(proxy_adresi):
    parca = urlsplit(proxy_adresi)
    return f"{parca.hostname}:{parca.port}"

class ProxyHavuzuMiddleware:
    """Her isteğe listeden sıradaki proxy'yi atar, art arda hata vereni dinlendirir."""

    def __init__(self, adresler, hata_siniri, dinlenme, stats):
        self.adresler = adresler
        self.hata_siniri = hata_siniri
        self.dinlenme = dinlenme
        self.stats = stats
        self.sira = 0
        self.hatalar = {anahtar(a): 0 for a in adresler}
        self.kapali = {}  # anahtar -> yeniden açılacağı an

    @classmethod
    def from_crawler(cls, crawler):
        adresler = crawler.settings.getlist("PROXY_LISTESI")
        if not adresler:
            raise NotConfigured
        return cls(
            adresler,
            crawler.settings.getint("PROXY_HATA_SINIRI", 3),
            crawler.settings.getfloat("PROXY_DINLENME", 60.0),
            crawler.stats,
        )

    def sec(self):
        simdi = time.monotonic()
        for _ in self.adresler:
            adres = self.adresler[self.sira % len(self.adresler)]
            self.sira += 1
            if self.kapali.get(anahtar(adres), 0) <= simdi:
                return adres
        return None

    def process_request(self, request):
        adres = self.sec()
        if adres is None:
            self.stats.inc_value("proxy_havuzu/hepsi_dinleniyor")
            raise IgnoreRequest("Listedeki bütün proxy'ler dinleniyor")
        request.meta["proxy"] = adres
        request.meta["proxy_anahtari"] = anahtar(adres)
        return None

    def process_response(self, request, response):
        kim = request.meta.get("proxy_anahtari")
        if kim:
            self.hatalar[kim] = 0
            self.stats.inc_value(f"proxy_havuzu/yanit/{kim}")
        return response

    def process_exception(self, request, exception):
        kim = request.meta.get("proxy_anahtari")
        if not kim:
            return None
        self.hatalar[kim] += 1
        self.stats.inc_value(f"proxy_havuzu/hata/{kim}")
        if self.hatalar[kim] >= self.hata_siniri:
            self.kapali[kim] = time.monotonic() + self.dinlenme
            self.hatalar[kim] = 0
        return None
```

```python
# kitaplik/settings.py
PROXY_LISTESI = [
    "http://user:pass@203.0.113.10:8000",
    "http://user:pass@203.0.113.11:8000",
    "http://user:pass@203.0.113.12:8000",
]
DOWNLOADER_MIDDLEWARES = {
    "kitaplik.middlewares.ProxyHavuzuMiddleware": 610,
}
```

Örneği ikisi çalışan, biri kapalı üç yerel proxy ile denedik. Yüz kayıt eksiksiz geldi; istekler iki çalışan adrese 6 ve 5 olarak dağıldı, kapalı adres üç bağlantı hatasından sonra devreden çıktı ve hata alan istekleri Scrapy öbür adreslerden yeniden denedi.

Sıra numarası burada keyfî değildir. İlk denemede middleware'i 350'ye koyduk ve hata sayacı hiç çalışmadı: istisnalar zincirde ters sırayla dolaşır, 550'deki yeniden deneme middleware'i istisnayı yakalayıp yeni bir istek döndürdüğünde daha küçük numaralı middleware'ler haberdar olmaz. 550 ile 750 arasında bir değer (örnekte 610) iki koşulu birden sağlar: hatayı yeniden denemeden önce siz görürsünüz, kimlik bilgisini ise sizden sonra `HttpProxyMiddleware` işler. Aynı işi Requests ile elle yapmanın yolu [Python'da Proxy'ler Nasıl Döndürülür?](/tr/blog/rotating-proxy-python) yazımızda.

## DOWNLOAD_DELAY ve AutoThrottle nasıl ayarlanır?

Proxy, isteğin nereden çıktığını değiştirir; hedef sunucuya binen yükü değiştirmez. Yükü belirleyen ayarlar şunlardır:

```python
# kitaplik/settings.py
USER_AGENT = "kitaplik-bot/1.0 (+https://ornek.com/bot)"
ROBOTSTXT_OBEY = True

CONCURRENT_REQUESTS = 8
CONCURRENT_REQUESTS_PER_DOMAIN = 2
DOWNLOAD_DELAY = 1
DOWNLOAD_TIMEOUT = 30

AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 2
AUTOTHROTTLE_MAX_DELAY = 30
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
AUTOTHROTTLE_DEBUG = False

RETRY_ENABLED = True
RETRY_TIMES = 2
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 408]
```

- **`CONCURRENT_REQUESTS_PER_DOMAIN`** aynı alan adına aynı anda açık olabilecek istek sayısıdır. Çatının varsayılanı 8'dir, proje şablonu bunu 1'e indirir. Eşzamanlılığın ne olduğunu [Concurrency ve Parallelism](/tr/blog/concurrency-vs-parallelism) yazımızda anlattık.
- **`DOWNLOAD_DELAY`** aynı alan adına giden iki istek arasındaki beklemedir (saniye). Scrapy bu değere varsayılan olarak rastgele bir oynama ekler; istekler saat gibi eşit aralıklarla gitmez.
- **AutoThrottle** beklemeyi sunucunun yanıt süresine göre ayarlar. [Belgedeki algoritmaya](https://docs.scrapy.org/en/latest/topics/autothrottle.html) göre hedef bekleme, yanıt gecikmesinin `AUTOTHROTTLE_TARGET_CONCURRENCY` değerine bölümüdür; yeni bekleme, önceki beklemeyle hedefin ortalamasıdır. `200` dışındaki yanıtlar beklemeyi düşüremez. Bekleme hiçbir zaman `DOWNLOAD_DELAY`'in altına inmez, `AUTOTHROTTLE_MAX_DELAY`'in üstüne çıkmaz.
- **`DOWNLOAD_TIMEOUT`** varsayılanı 180 saniyedir. Proxy üzerinden çalışırken yanıt vermeyen bir bağlantının üç dakika asılı kalmaması için kısaltın.

`AUTOTHROTTLE_DEBUG = True` yaptığınızda her yanıtta bir satır görürsünüz. Bizim denememizde bekleme 2.000 ms ile başladı, sunucu 145 ms'de yanıt verdikçe 1.072 ms'ye, sonra `DOWNLOAD_DELAY` tabanı olan 1.000 ms'ye indi ve orada kaldı:

```text
slot: books.toscrape.com | conc: 1 | delay: 2000 ms (+0)   | latency: 446 ms
slot: books.toscrape.com | conc: 1 | delay: 1072 ms (-927) | latency: 145 ms
slot: books.toscrape.com | conc: 1 | delay: 1000 ms (-72)  | latency: 144 ms
```

`ROBOTSTXT_OBEY` açıkken Scrapy her alan adı için önce `/robots.txt` dosyasını indirir ve izin verilmeyen adresleri istemeden eler. Dosyanın nasıl okunduğu [robots.txt Dosyası Nedir, Nasıl Okunur?](/tr/blog/robots-txt) yazımızda, taramanın hukuki çerçevesi [Web Scraping (Web Kazıma) Yasal mı?](/tr/blog/web-scraping-legal) yazımızda. `USER_AGENT` alanına size ulaşılabilecek bir adres yazmak, site yöneticisinin sorun gördüğünde sizi engellemek yerine size yazabilmesini sağlar.

## Scrapy 429 ve 503 yanıtlarında ne yapar?

Yeniden deneme middleware'i varsayılan olarak `500`, `502`, `503`, `504`, `522`, `524`, `408` ve `429` kodlarını ve bağlantı hatalarını yeniden dener; `RETRY_TIMES = 2` ilk istekle birlikte toplam üç deneme demektir. Yeniden denenen istek kuyruğa daha düşük öncelikle geri döner.

İki sınırı bilmek gerekir. Scrapy'nin yeniden denemesi `Retry-After` başlığını okumaz ve denemeler arasında üstel bekleme uygulamaz; aradaki süreyi yine `DOWNLOAD_DELAY` ve AutoThrottle belirler. AutoThrottle `200` dışındaki yanıtlarda beklemeyi düşürmediği için `429` dalgasında hız kendiliğinden artmaz, ama otomatik olarak yeterince düşmeyebilir de. Günlükte `429` oranı yükseliyorsa doğru tepki `CONCURRENT_REQUESTS_PER_DOMAIN` değerini indirmek ve `DOWNLOAD_DELAY`'i büyütmektir. Hız sınırını daha çok IP ile aşmaya çalışmak sorunu çözmez, siteye binen yükü artırır. Kodların anlamı ve `Retry-After` ile doğru bekleme [Scraping'de HTTP Hata Kodları](/tr/blog/http-status-codes-web-scraping) yazımızda, hız sınırının site tarafındaki mantığı [429 Too Many Requests](/tr/blog/http-429-too-many-requests) yazımızda.

## Scrapy'de SOCKS5 proxy kullanılabilir mi?

Varsayılan indiriciyle hayır. `meta["proxy"]` alanına `socks5://` adresi yazdığımız denemede istek hiçbir yanıt almadan zaman aşımına kadar bekledi. Scrapy'nin httpx tabanlı ikinci indiricisi `HttpxDownloadHandler` ise 2.17 sürümünden beri SOCKS5'i destekler. Kurulum için `pip install "scrapy[httpx]"` komutunu çalıştırıp ayarlara şunu ekleyin:

```python
DOWNLOAD_HANDLERS = {
    "http": "scrapy.core.downloader.handlers._httpx.HttpxDownloadHandler",
    "https": "scrapy.core.downloader.handlers._httpx.HttpxDownloadHandler",
}
```

Bu ayarla `socks5://user:pass@…` adresi testimizde kimlik doğrulamayla birlikte çalıştı. Scrapy belgeleri bu indiriciyi deneysel olarak işaretliyor ve üretimde kullanılmasını henüz önermiyor; her proxy adresi için ayrı bağlantı havuzu açtığını da not ediyor. Zorunlu değilseniz HTTP proxy ile kalın. Protokol farkları [SOCKS ve HTTP Proxy Farkı](/tr/blog/socks-vs-http-proxy) yazımızda.

## JavaScript ile yüklenen sayfalarda ne yapılır?

Scrapy sayfanın HTML'ini indirir, JavaScript çalıştırmaz. Veri tarayıcıda sonradan yükleniyorsa önce sayfanın arka planda çağırdığı JSON uç noktasına bakın; çoğu zaman Scrapy ile doğrudan o adres istenebilir. Bu mümkün değilse [scrapy-playwright](https://github.com/scrapy-plugins/scrapy-playwright) eklentisi seçtiğiniz istekleri gerçek bir tarayıcıda açar. Dikkat: bu eklentide proxy `meta["proxy"]` ile değil, tarayıcı başlatma ya da bağlam (context) seçenekleriyle verilir. Ayrıntısı [Playwright Nedir ve Proxy ile Nasıl Kullanılır?](/tr/blog/playwright-proxy) yazımızda, sayfa türlerini ayırt etmenin yolu [Statik ve Dinamik Sayfalar](/tr/blog/static-vs-dynamic-pages) yazımızda.

## Scrapy mi, BeautifulSoup mu, Selenium mu?

Üçü aynı işin farklı parçalarıdır; karşılaştırma bu yüzden çoğu zaman yanlış kurulur.

| | Scrapy | Requests + BeautifulSoup | Selenium |
|---|---|---|---|
| Ne olduğu | Tarama çatısı | HTTP istemcisi + HTML ayrıştırıcı | Tarayıcı otomasyonu |
| İstek kuyruğu ve eşzamanlılık | Hazır | Siz yazarsınız | Siz yazarsınız |
| Yeniden deneme, hız sınırı, robots.txt | Ayarla açılır | Siz yazarsınız | Siz yazarsınız |
| JavaScript çalıştırma | Yok (eklentiyle) | Yok | Var |
| Proxy tanımı | `meta["proxy"]` ya da middleware | `proxies=` parametresi | Tarayıcı başlatma seçeneği |
| Öğrenme eşiği | Orta | Düşük | Orta |
| Uygun iş | Çok sayfalı, tekrarlanan tarama | Tek seferlik, birkaç sayfa | Giriş, tıklama, dinamik içerik |

BeautifulSoup yalnızca bir ayrıştırıcıdır ve Scrapy'nin içinde de kullanılabilir. Selenium ise her sayfa için tam bir tarayıcı açtığından aynı donanımda çok daha az sayfa işler; yalnızca gerçekten tarayıcı gereken adımlarda kullanılması mantıklıdır. Selenium tarafındaki proxy kurulumu [Selenium Proxy Entegrasyonu](/tr/blog/selenium) yazımızda, iki tarayıcı aracının karşılaştırması [Playwright ve Selenium Farkı](/tr/blog/playwright-vs-selenium) yazımızda.

## Kullanım alanları

- **Fiyat ve stok takibi:** Aynı ürün listesi her gün taranır; Scrapy'nin zamanlanmış çalıştırmaya uygun yapısı bu işe oturur. Kurgu [fiyat takibi çözümü](/tr/price-monitoring) sayfamızda, çalışan bir örnek [E-Ticarette Rakip Fiyat Takibi Nasıl Yapılır?](/tr/blog/competitor-price-tracking) yazımızda.
- **Site içi tarama ve dizin çıkarma:** Bağlantıları izleyerek bütün sayfaları dolaşan `CrawlSpider` sınıfı, [web crawler](/tr/web-crawler) işleri için hazır bir başlangıçtır.
- **Çok siteli katalog toplama:** Her site için ayrı spider, ortak pipeline ve ortak proxy ayarı. Genel mimari [veri kazıma çözümü](/tr/data-scraping) sayfamızda.
- **Sayfalanmış listeler:** "Sonraki" bağlantısı, sayfa numarası ve imleç (cursor) kalıpları [Sayfalama (Pagination)](/tr/blog/pagination-web-scraping) yazımızda.

## Sık yapılan hatalar

- **Proxy'yi yalnızca ilk isteğe yazmak.** `meta` sonraki isteklere aktarılmaz; tarama ikinci sayfadan itibaren kendi IP adresinizden devam eder ve bunu günlükten fark etmezsiniz.
- **Middleware sıra numarasını rastgele seçmek.** 750'den büyük numarada kimlik bilgisi ayrıştırılmaz ve istek `invalid hostname` hatasıyla düşer, 550'den küçük numarada bağlantı hatalarını göremezsiniz.
- **Şablondaki `ROBOTSTXT_OBEY`, `DOWNLOAD_DELAY` ve eşzamanlılık satırlarını silmek.** Çatının çıplak varsayılanları (robots.txt kapalı, bekleme yok, alan adı başına 8 istek) küçük bir siteyi zorlayacak kadar hızlıdır.
- **`407` alırken taramayı sürdürmek.** Her istek üç kez denenir, hiçbiri başarılı olmaz; önce kimlik bilgisini düzeltin.
- **Rotating gateway'in üstüne ayrıca rotasyon middleware'i kurmak.** Ağ geçidi bu işi zaten yapar; ikinci katman yalnızca hata ayıklamayı zorlaştırır.
- **`DOWNLOAD_TIMEOUT`'u varsayılanda bırakmak.** Yanıt vermeyen tek bir bağlantı bir yuvayı üç dakika meşgul eder.
- **Parolayı `settings.py` içine yazıp depoya göndermek.** Adresi ortam değişkeninden okuyun.

## Karar rehberi

| İhtiyaç | Öneri |
|---|---|
| Birkaç sayfalık tek seferlik iş | Requests + BeautifulSoup yeterli, Scrapy şart değil |
| Binlerce sayfa, düzenli tekrar | Scrapy projesi, şablon ayarları korunarak |
| Bütün trafiğin proxy'den geçmesi | `TekProxyMiddleware` ya da `https_proxy` ortam değişkeni |
| Yalnızca belirli isteklerin farklı çıkıştan gitmesi | O isteklerde `meta["proxy"]` |
| Çok sayıda IP'ye yük dağıtma | Rotating gateway, tek adres, middleware yok |
| Elinizde sabit proxy listesi var | `ProxyHavuzuMiddleware`, sıra numarası 550-750 arası |
| Oturum boyunca aynı IP | [Sticky Proxy](https://proxynet.io/tr/sticky-proxy) ve tek adres |
| Günlükte `429` artıyor | Eşzamanlılığı düşürün, `DOWNLOAD_DELAY`'i büyütün, AutoThrottle'ı açın |
| SOCKS5 zorunlu | `HttpxDownloadHandler` (deneysel) |
| Veri JavaScript ile geliyor | Önce JSON uç noktası, olmazsa scrapy-playwright |

## Sıkça sorulan sorular

### Scrapy spider nedir?

Spider, `scrapy.Spider` sınıfından türeyen ve iki şeyi tanımlayan bir Python sınıfıdır: taramanın hangi adreslerden başlayacağı ve gelen yanıttan hangi verinin, hangi yeni bağlantıların çıkarılacağı. Kuyruk, indirme ve hata yönetimi spider'ın değil çatının işidir.

### Scrapy kullanımı için Python bilmek şart mı?

Evet. Spider bir Python sınıfıdır ve seçicileri, döngüleri, sözlükleri kullanırsınız. Temel Python bilen biri ilk spider'ını yazabilir; middleware ve pipeline tarafı için sınıf yapısına aşina olmak gerekir.

### Proxy'nin gerçekten kullanıldığını nasıl anlarım?

IP adresinizi JSON olarak döndüren bir servise Scrapy ile istek gönderin ve yanıttaki adresi kendi IP'nizle karşılaştırın. `parse` içinde `response.meta.get("proxy")` değerini günlüğe yazmak da hangi isteğin hangi proxy'den gittiğini gösterir. Genel yöntemler [Proxy Nasıl Test Edilir?](/tr/blog/how-to-test-a-proxy) yazımızda.

### Scrapy mi Selenium mu daha hızlı?

Aynı donanımda Scrapy belirgin biçimde daha çok sayfa işler, çünkü tarayıcı açmaz ve istekleri asenkron gönderir. Hız ancak sayfa JavaScript gerektirmiyorsa anlamlıdır; veri tarayıcıda oluşuyorsa Scrapy tek başına o veriyi göremez.

### AutoThrottle açıkken DOWNLOAD_DELAY gereksiz mi olur?

Hayır, tabanı o belirler. AutoThrottle beklemeyi hiçbir zaman `DOWNLOAD_DELAY`'in altına indirmez. Hızlı yanıt veren bir sunucuda bekleme bu tabana iner ve orada kalır; bu yüzden `DOWNLOAD_DELAY = 0` ile AutoThrottle'a güvenmek, hızlı sunucularda neredeyse beklemesiz istek göndermek demektir.

### Her istekte farklı IP kullanmak engellenmeyi önler mi?

Tek başına önlemez. Siteler hızı yalnızca IP'ye göre değil, oturuma, çereze ve davranışa göre de sayar. Rotasyon yükü birçok çıkışa dağıtır; robots.txt'ye uymak, düşük eşzamanlılık ve dürüst bir `User-Agent` ile birlikte anlam kazanır. Meşru yöntemleri [Web Scraping'de Engellenmeden Veri Toplama Yöntemleri](/tr/blog/web-scraping-without-getting-blocked) yazımızda topladık.

## Özetle

Scrapy'de proxy, isteğin `meta["proxy"]` alanıdır ve bu alanı üç yoldan doldurabilirsiniz: istek başına elle, ortam değişkeniyle ya da bir downloader middleware ile. Bütün trafiğin, robots.txt dahil, aynı çıkıştan gitmesi için son iki yoldan biri gerekir. Rotating gateway kullanıyorsanız tek adres yeter; sabit bir listeniz varsa 550-750 arasında numaralanmış bir middleware dağıtımı ve hata sayımını üstlenir. Hızı proxy değil `DOWNLOAD_DELAY`, alan adı başına eşzamanlılık ve AutoThrottle belirler; şablonun getirdiği temkinli ayarları koruyun. Tarama işlerinize uygun proxy türlerini [proxy hizmetlerimizde](/tr/proxy) bulabilirsiniz.
