# Middleware proxy Scrapy untuk rotasi proxy residensial

> Middleware proxy rotasi Scrapy: rotasi dan sesi tetap IP residensial per permintaan, AutoThrottle dan pengaturan retry, scrapy-playwright. Kode sudah dicek sintaksnya.

- URL: https://crawlrelay.com/id/docs/scrapy/
- Terakhir diperbarui: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Middleware downloader sepanjang 30 baris yang memilih negara dan sesi per permintaan, serta pengaturan agar crawl tetap sopan: robots.txt, konkurensi per domain, AutoThrottle dan retry.

**Jawaban cepat — Bagaimana menggunakan proxy rotasi dengan Scrapy?**

Atur `request.meta["proxy"]` ke `http://USERNAME:PASSWORD@gw.crawlrelay.com:7777` dalam middleware downloader kecil yang dijalankan sebelum `HttpProxyMiddleware` Scrapy; Scrapy lalu mengirimkan kredensial untuk Anda. Dengan proxy residensial **CrawlRelay**, negara dan sesi tetap masuk dalam username, sehingga satu middleware menangani keduanya. Biaya trafik US$0,50/GB mulai dari 50 GB. Dibayar dengan crypto, tanpa KYC.

## Setup Scrapy secara singkat

| Atribut | Nilai |
| --- | --- |
| Dimana letak proxy | `request.meta["proxy"]`, disetel oleh middleware Anda |
| Urutan Middleware | Middleware Anda di bawah 750, prioritas middleware `HttpProxyMiddleware` bawaan |
| Kredensial | Dalam URL proxy; Scrapy mengubahnya menjadi header `Proxy-Authorization` |
| SOCKS5 | Tidak dengan handler HTTP/1.1 default; Scrapy 2.17+ mendukung SOCKS lewat HttpxDownloadHandler opsional. Port HTTP 7777 paling sederhana |
| Rotasi | Exit baru per koneksi baru; ID sesi unik per permintaan biasanya memberi IP berbeda |
| Kepatuhan Sopan santun | `ROBOTSTXT_OBEY`, `CONCURRENT_REQUESTS_PER_DOMAIN`, `AUTOTHROTTLE_ENABLED` |
| Retry | 429 dan 5xx ada dalam `RETRY_HTTP_CODES` default; Retry-After tidak dibaca secara default |

## Bagaimana cara menulis middleware proxy?

Letakkan kelas di `middlewares.py`. Kelas membangun username dari pengaturan dan dari tiga kunci `meta` opsional: `proxy_country`, `proxy_session` dan `proxy_ttl`. Permintaan tanpa `proxy_session` akan berotasi; permintaan dengan `proxy_session="new"` mendapat sesi baru, jadi biasanya IP exit berbeda; permintaan dengan ID sesi tetap berbagi satu exit dengan permintaan lain dalam alur yang sama. Middleware menghitung ulang proxy setiap kali dilewati, termasuk retry, dan membiarkan proxy yang disetel kode lain tetap ada. Jika `CRAWLRELAY_USER` hilang, crawl berhenti saat mulai alih-alih berjalan dari IP Anda sendiri.

*middlewares.py*

```python
import uuid
from urllib.parse import quote


class CrawlRelayProxyMiddleware:
    """Sets request.meta["proxy"]; Scrapy's HttpProxyMiddleware then sends the credentials."""

    def __init__(self, user, password, country):
        self.user, self.password, self.country = user, password, country

    @classmethod
    def from_crawler(cls, crawler):
        s = crawler.settings
        if not s.get("CRAWLRELAY_USER"):
            # Fail loudly: NotConfigured would only disable the middleware and crawl from your own IP
            raise ValueError("CRAWLRELAY_USER is not set: refusing to crawl without the proxy")
        return cls(s.get("CRAWLRELAY_USER"), s.get("CRAWLRELAY_PASSWORD", ""), s.get("CRAWLRELAY_COUNTRY", "us"))

    def process_request(self, request, spider=None):
        if "proxy" in request.meta and not request.meta.get("_crawlrelay"):
            return None  # a proxy set by other code: leave it alone
        parts = [self.user, "country", request.meta.get("proxy_country", self.country)]
        session = request.meta.get("proxy_session")
        if session == "new":  # a fresh session for this request (and for each of its retries)
            session = uuid.uuid4().hex[:12]
        if session:
            parts += ["session", session, "ttl", str(request.meta.get("proxy_ttl", 10))]
        username = "-".join(parts)
        request.meta["proxy"] = "http://%s:%s@gw.crawlrelay.com:7777" % (
            quote(username, safe=""), quote(self.password, safe=""))
        request.meta["_crawlrelay"] = True
        return None
```

## Pengaturan Scrapy mana yang menjaga crawl memakai proxy tetap sopan dan cepat?

Proxy menyebarkan permintaan Anda ke banyak IP, tapi target masih melihat total kecepatan Anda. Pengaturan ini menjaga tiap domain berjalan dengan kecepatan yang wajar dan membiarkan AutoThrottle memperlambat sendiri saat respon melambat. Pertahankan `ROBOTSTXT_OBEY = True`, yang sudah disetel oleh proyek Scrapy baru.

*settings.py (kutipan)*

```python
import os

BOT_NAME = "myproject"
ROBOTSTXT_OBEY = True
USER_AGENT = "ExampleCrawler/1.0 (+https://example.com/crawler-info)"

CRAWLRELAY_USER = os.environ.get("CRAWLRELAY_USER")  # e.g. the username from your dashboard
CRAWLRELAY_PASSWORD = os.environ.get("CRAWLRELAY_PASSWORD")
CRAWLRELAY_COUNTRY = "us"

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.CrawlRelayProxyMiddleware": 350,  # runs before HttpProxyMiddleware (750)
}

CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 4
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 30.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504, 522, 524, 408]
DOWNLOAD_TIMEOUT = 45
```

## Bagaimana cara menjaga satu IP untuk pagination dan merotasi untuk halaman item?

Lewatkan `proxy_session` tetap pada permintaan dari aliran yang bergantung pada halaman sebelumnya, dan bukan pada halaman independen. Spider di bawah ini berjalan di books.toscrape.com, situs sandbox yang diterbitkan untuk latihan scraping.

*spiders/catalog.py*

```python
import scrapy


class CatalogSpider(scrapy.Spider):
    name = "catalog"
    start_urls = ["https://books.toscrape.com/"]

    def parse(self, response):
        for href in response.css("article.product_pod h3 a::attr(href)").getall():
            # Product pages are independent: rotating exits
            yield response.follow(href, callback=self.parse_item)
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            # The listing flow keeps one sticky exit for 30 minutes
            yield response.follow(next_page, callback=self.parse,
                                  meta={"proxy_session": "listing1", "proxy_ttl": 30})

    def parse_item(self, response):
        yield {
            "title": response.css("div.product_main h1::text").get(),
            "price": response.css("p.price_color::text").get(),
            "url": response.url,
        }
```

## Apakah Scrapy menggunakan kembali koneksi, dan apakah itu menghentikan rotasi?

Ya, Scrapy menjaga koneksi tetap hidup dan menggunakannya kembali per proxy dan host target, jadi permintaan berturut-turut pada satu koneksi berbagi exit. Untuk sebagian besar crawl itu tidak masalah: Scrapy membuka hingga `CONCURRENT_REQUESTS_PER_DOMAIN` koneksi per situs, dan tiap koneksi baru biasanya mendapatkan IP proxy residensial yang berbeda.

Saat permintaan harus keluar melalui IP baru, misalnya retry setelah mendapatkan [502](https://crawlrelay.com/id/docs/errors/502-bad-gateway/) dari exit yang gagal, kirim ulang dengan `response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True)`. Middleware membuat username sesi baru, username berbeda berarti kolam koneksi berbeda, jadi Scrapy membuka koneksi baru dan gateway biasanya menetapkan exit berbeda.

## Bagaimana cara menghormati Retry-After pada respons 429 di Scrapy?

Scrapy built-in `RetryMiddleware` mencoba ulang untuk 429 tapi tidak menunggu delay `Retry-After`. AutoThrottle memperlambat domain secara bertahap, yang biasanya cukup. Untuk API dengan nilai `Retry-After` yang panjang, tambahkan middleware kecil yang membaca header dan menjadwalkan retry dengan delay, atau turunkan `CONCURRENT_REQUESTS_PER_DOMAIN` untuk domain tersebut. Halaman [429](https://crawlrelay.com/id/docs/errors/429-too-many-requests/) menjelaskan arti header tersebut.

## Bagaimana cara menggunakan proxy dengan scrapy-playwright?

Untuk halaman yang membutuhkan JavaScript, scrapy-playwright meneruskan opsi peluncuran Playwright melalui setting. Berikan kredensial sebagai field terpisah: Chromium mengabaikan kredensial yang tertulis di dalam URL server. Gunakan satu konteks per sesi sticky jika kamu merender alur logout yang mencakup beberapa halaman.

*settings.py untuk scrapy-playwright*

```python
import os

DOWNLOAD_HANDLERS = {
    "http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
    "https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"

PLAYWRIGHT_LAUNCH_OPTIONS = {
    "proxy": {
        "server": "http://gw.crawlrelay.com:7777",
        "username": os.environ.get("CRAWLRELAY_USER", "USERNAME") + "-country-us",
        "password": os.environ.get("CRAWLRELAY_PASSWORD", "PASSWORD"),
    },
}
```

## Scrapy dan proxy: pertanyaan

### Apakah saya perlu scrapy-rotating-proxies atau daftar proxy?

Tidak. Paket tersebut merotasi daftar alamat proxy individual dan memblokir yang gagal. Dengan gateway proxy residensial, rotasi terjadi di balik satu alamat: setiap koneksi baru biasanya mendapatkan IP proxy residensial berbeda. Middleware tunggal yang mengatur `request.meta["proxy"]` sudah cukup.

### Mengapa Scrapy menampilkan TunnelError dengan proxy saya?

`TunnelError: Could not open CONNECT tunnel` berarti proxy menolak atau gagal permintaan CONNECT untuk situs HTTPS. Status pada pesan memberi tahu alasannya: 407 terkait kredensial, 502 atau 504 terkait exit atau target. Lihat halaman [407](https://crawlrelay.com/id/docs/errors/407-proxy-authentication-required/) dan [502](https://crawlrelay.com/id/docs/errors/502-bad-gateway/).

### Berapa banyak permintaan bersamaan per domain yang sebaiknya saya gunakan?

Mulai dengan 2 sampai 4 per domain dan biarkan AutoThrottle menyesuaikan. Tingkatkan jumlah hanya jika persentase respons 429, 403, dan 5xx tetap stabil, dan perhatikan waktu respons situs. Situs besar toleran lebih banyak dibanding yang kecil; situs kecil mungkin perlu jeda antar permintaan dan satu koneksi.

### Haruskah saya menonaktifkan ROBOTSTXT_OBEY untuk mendapatkan lebih banyak data?

Tidak. robots.txt adalah cara situs menyatakan jalur yang boleh diambil oleh klien otomatis, dan RFC 9309 menstandarisasi format ini sejak 2022. Mengabaikannya meningkatkan risiko hukum dan etika dan melanggar [Acceptable Use Policy](https://crawlrelay.com/id/acceptable-use/) kami jika digunakan untuk mengakses konten yang dikeluarkan situs.

### Berapa banyak trafik yang digunakan oleh crawl Scrapy?

Scrapy hanya mengunduh HTML, jadi halaman biasanya berukuran puluhan kilobyte tiap halaman, terkompresi. Periksa nilai `downloader/response_bytes` dalam statistik yang Scrapy cetak di akhir run dan bagi dengan jumlah respons. Panduan [bandwidth](https://crawlrelay.com/id/guides/bandwidth-calculator/) mengubahnya menjadi perkiraan biaya.

**Pasang IP proxy residensial ke proyek Scrapy kamu** US$5,94 untuk 10 GB untuk pengujian, US$0,50/GB dari 50 GB. [Lihat harga](https://crawlrelay.com/id/pricing/) · [Panduan concurrency](https://crawlrelay.com/id/docs/concurrency/)

---

CrawlRelay: CrawlRelay adalah layanan proxy residensial dan proxy seluler 4G/5G yang berfokus pada pengembang untuk scraper dan pipeline data AI: trafik pay-as-you-go tanpa kedaluwarsa mulai dari US$0,50/GB, dibayar dengan crypto, tanpa perlu unggah ID. Tanpa KYC — tanpa ID, tanpa selfie, tanpa dokumen. Bayar dengan crypto.
