# Middleware proxy dla rotujących proxy rezydencjalnych w Scrapy

> Middleware rotujący proxy w Scrapy: rotujące i „sticky” proxy rezydencjalne na żądanie, ustawienia AutoThrottle i retry, scrapy-playwright. Kod sprawdzony składniowo.

- URL: https://crawlrelay.com/pl/docs/scrapy/
- Ostatnia aktualizacja: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Middleware downloader o długości 30 linii, który wybiera kraj i sesję na żądanie oraz ustawienia dbające o grzeczne crawl: robots.txt, limit współbieżności na domenę, AutoThrottle i retry.

**Szybka odpowiedź — Jak korzystać z rotujących proxy rezydencjalnych w Scrapy?**

Ustaw `request.meta["proxy"]` na `http://USERNAME:PASSWORD@gw.crawlrelay.com:7777` w małym middleware downloadera uruchamianym przed Scrapy’s `HttpProxyMiddleware`; Scrapy wtedy sam wysyła poświadczenia. Przy użyciu **CrawlRelay** proxy rezydencjalnych kraj i sticky session koduje się w nazwie użytkownika, więc jedno middleware obsługuje oba. Ruch kosztuje 0,50 USD/GB od 50 GB. Płatność w krypto, bez KYC.

## Podstawowa konfiguracja Scrapy

| Atrybut | Wartość |
| --- | --- |
| Gdzie podać proxy | `request.meta["proxy"]`, ustawione przez twoje middleware |
| Kolejność middleware | Twoje middleware poniżej 750, co jest priorytetem wbudowanego `HttpProxyMiddleware` |
| Dane uwierzytelniające | W URL proxy; Scrapy zamienia je na nagłówek `Proxy-Authorization` |
| SOCKS5 | Nie z domyślnym handlerem HTTP/1.1; Scrapy 2.17+ obsługuje SOCKS przez opcjonalny HttpxDownloadHandler. Port HTTP 7777 jest najprostszy |
| Rotacja | Nowe wyjście na nowe połączenie; unikatowe ID sesji na żądanie zwykle daje inny IP |
| Grzeczność | `ROBOTSTXT_OBEY`, `CONCURRENT_REQUESTS_PER_DOMAIN`, `AUTOTHROTTLE_ENABLED` |
| Retry | 429 i 5xx są w domyślnym `RETRY_HTTP_CODES`; Retry-After domyślnie nie jest brane pod uwagę |

## Jak napisać middleware proxy?

Umieść klasę w `middlewares.py`. Buduje nazwę użytkownika z ustawień i z trzech opcjonalnych kluczy `meta`: `proxy_country`, `proxy_session` i `proxy_ttl`. Żądanie bez `proxy_session` powoduje rotację; z `proxy_session="new"` tworzy nową sesję, więc normalnie inny exit IP; z ustalonym ID sesji dzieli jedno wyjście z innymi żądaniami tego samego flow. Middleware oblicza proxy przy każdym przejściu, łącznie z retry, i nie zmienia proxy ustawionego gdzie indziej. Jeśli brakuje `CRAWLRELAY_USER`, crawl zatrzymuje się na starcie zamiast działać z twojego IP.

*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
```

## Jakie ustawienia Scrapy utrzymują proxied crawl grzecznym i szybkim?

Proxy rozkładają twoje żądania na wiele IP, ale cel widzi łączną liczbę. Te ustawienia utrzymują każdy domenę w zrównoważonym tempie i pozwalają AutoThrottle zwolnić samemu, gdy reakcje się wydłużają. Zachowaj `ROBOTSTXT_OBEY = True`, które nowe projekty Scrapy już mają.

*settings.py (fragment)*

```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
```

## Jak utrzymać ten sam IP dla paginacji i rotować dla stron z elementami?

Przekaż stałe `proxy_session` w żądaniach przepływu zależnego od poprzedniej strony, a żadnego na stronach niezależnych. Pająk poniżej działa na books.toscrape.com, stronie-sandbox opublikowanej do ćwiczeń z web scrapingu.

*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,
        }
```

## Czy Scrapy ponownie wykorzystuje połączenia i czy to przerywa rotację?

Tak, Scrapy utrzymuje połączenia aktywne i ponownie je wykorzystuje dla proxy rezydencjalne oraz docelowego hosta, więc kolejne żądania na jednym połączeniu korzystają z tego samego adresu wyjścia. Dla większości crawli jest to w porządku: Scrapy otwiera do `CONCURRENT_REQUESTS_PER_DOMAIN` połączeń na stronę, a każde nowe połączenie zazwyczaj dostaje inny residential IP.

Gdy żądanie musi wyjść przez nowy IP, np. po ponownej próbie po błędzie [502](https://crawlrelay.com/pl/docs/errors/502-bad-gateway/) z problematycznego wyjścia, wyślij je ponownie z `response.request.replace(meta={**response.meta, "proxy_session": "new"}, dont_filter=True)`. Middleware tworzy nową nazwę użytkownika sesji, inna nazwa użytkownika oznacza inny pulę połączeń, więc Scrapy otwiera nowe połączenie, a brama zwykle przydziela inny exit.

## Jak honorować Retry-After na odpowiedziach 429 w Scrapy?

Wbudowany w Scrapy `RetryMiddleware` ponawia żądania 429, ale nie czeka na opóźnienie `Retry-After`. AutoThrottle stopniowo zwalnia domenę, co najczęściej wystarcza. Dla API zwracających długie wartości `Retry-After`, dodaj mały middleware, który odczytuje nagłówek i planuje ponowienie z opóźnieniem lub zmniejsz `CONCURRENT_REQUESTS_PER_DOMAIN` dla tej domeny. Strona [429](https://crawlrelay.com/pl/docs/errors/429-too-many-requests/) tłumaczy, co oznacza ten nagłówek.

## Jak używać proxy z scrapy-playwright?

Dla stron wymagających JavaScript scrapy-playwright przekazuje opcje uruchomienia Playwright przez ustawienia. Podaj dane uwierzytelniające jako oddzielne pola: Chromium ignoruje dane wpisane w adres serwera. Użyj jednego kontekstu na sticky session, jeśli renderujesz przepływy dla niezalogowanych użytkowników obejmujące kilka stron.

*settings.py dla 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 i proxy: pytania

### Czy potrzebuję scrapy-rotating-proxies lub listy proxy?

Nie. Te pakiety rotują listę indywidualnych adresów proxy i blokują te, które zawodzą. W przypadku residential gateway rotacja odbywa się za jednym adresem: każde nowe połączenie zwykle dostaje inny residential IP. Wystarczy jeden middleware ustawiający `request.meta["proxy"]`.

### Dlaczego Scrapy pokazuje TunnelError z moim proxy?

`TunnelError: Could not open CONNECT tunnel` oznacza, że proxy odmówiło lub nie powiodło się żądanie CONNECT do strony HTTPS. Status w komunikacie tłumaczy przyczynę: 407 to uwierzytelnianie, 502 lub 504 to exit lub cel. Zobacz strony [407](https://crawlrelay.com/pl/docs/errors/407-proxy-authentication-required/) oraz [502](https://crawlrelay.com/pl/docs/errors/502-bad-gateway/).

### Ile równoczesnych żądań na domenę powinienem używać?

Zacznij od 2 do 4 na domenę i pozwól AutoThrottle dostosować. Zwiększaj liczbę tylko wtedy, gdy udział odpowiedzi 429, 403 i 5xx pozostaje stały, i obserwuj czasy odpowiedzi serwisu. Duże serwisy tolerują więcej niż małe; mały serwis może wymagać opóźnienia między żądaniami i pojedynczego połączenia.

### Czy powinienem wyłączyć ROBOTSTXT_OBEY, żeby dostać więcej danych?

Nie. robots.txt to sposób, w jaki serwis informuje, które ścieżki mogą pobierać automatyczni klienci, a format ten został ustandaryzowany w RFC 9309 w 2022 roku. Ignorowanie go zwiększa Twoje ryzyko prawne i etyczne oraz narusza naszą [Politykę Akceptowalnego Użytkowania](https://crawlrelay.com/pl/acceptable-use/), gdy służy do dostępu do treści wykluczonych przez serwis.

### Ile ruchu generuje crawl Scrapy?

Scrapy pobiera tylko HTML, więc strony zwykle kosztują kilkadziesiąt kilobajtów każda, skompresowane. Sprawdź wartość `downloader/response_bytes` w statystykach, które Scrapy wyświetla na koniec działania, i podziel ją przez liczbę odpowiedzi. Przewodnik po [transferze danych](https://crawlrelay.com/pl/guides/bandwidth-calculator/) przelicza to na koszt.

**Podłącz residential IP do swojego projektu Scrapy** 5,94 USD dla 10 GB do testów, 0,50 USD/GB z 50 GB. [Zobacz ceny](https://crawlrelay.com/pl/pricing/) · [Przewodnik po współbieżności](https://crawlrelay.com/pl/docs/concurrency/)

---

CrawlRelay: CrawlRelay to serwis proxy rezydencjalne i mobilne proxy 4G/5G z rotacją, stworzony dla programistów i potoków danych AI: ruch bez wygasania w modelu pay-as-you-go od 0,50 USD/GB, płatny w kryptowalutach, bez konieczności przesyłania dokumentów. Bez KYC — brak dowodu, selfie i dokumentów. Płać w kryptowalutach.
