Twój spider Scrapy bez problemu przechodzi 100 testowych stron, a potem rozsypuje się przy 10 000. To wcale nie musi być błąd scrapingu — często to problem sieciowy, który tylko wygląda jak błąd scrapingu. Najczęściej sięga się wtedy po proxy, a wrzucenie go do request.meta zajmuje dosłownie jakieś 30 sekund.
Problem polega na tym, że na tym zwykle kończą się większość podstawowych poradników. Pokazują meta={"proxy": "http://IP:PORT"}, czasem dorzucają klasę middleware i uznają temat za zamknięty. Zazwyczaj pomijają to, co dzieje się, gdy proxy padnie w trakcie crawl'a, jak nie dopuścić do wycieku danych uwierzytelniających do historii Git, albo dlaczego ponowna próba może użyć dokładnie tego samego, wadliwego proxy. To właśnie są kwestie produkcyjne, które omawia ten przewodnik: konfiguracja fail-closed, zarządzanie sekretami, świadomy dobór proxy przy retry, ograniczenia protokołów i mierzalne kompromisy kosztowe.
Czym jest proxy middleware w Scrapy?
Proxy middleware to kawałek kodu, który działa w potoku pobierania danych Scrapy i decyduje, z jakiego adresu IP ma wyglądać dany request, zanim trafi on do sieci. Scrapy ma wbudowane rozwiązanie, HttpProxyMiddleware, które odczytuje klucz proxy z request.meta, dodaje autoryzację proxy, jeśli jest potrzebna, i przekazuje request do download handlera, który faktycznie otwiera połączenie. Własne proxy middleware nie zastępuje tego etapu transportu — ono jest selektorem, który wybiera proxy, zanim wbudowany mechanizm przejmie sterowanie. Ta różnica jest ważniejsza, niż może się wydawać, i właśnie ona stoi za większością zgłoszeń w stylu „moje custom middleware nie działa”.
Dlaczego proxy ma znaczenie w projektach Scrapy produkcyjnie
Proxy zmienia trasę sieciową i widoczny adres IP. Może pomóc w legalnych testach geolokalizacyjnych, rozdzielaniu autoryzowanego ruchu i izolowaniu awarii sieci. Nie daje jednak pozwolenia, nie omija limitów ani nie gwarantuje dostępu. To, czy crawl w ogóle potrzebuje proxy, zależy od celu, jego warunków, częstotliwości zapytań i wymagań dotyczących niezawodności.
Nie każde proxy jest takie samo, a wybór złego typu to osobna awaria produkcyjna:
| Typ proxy | Niezawodność | Szybkość | Ryzyko wykrycia | Typowy koszt |
|---|---|---|---|---|
| Darmowe publiczne proxy | Bardzo zmienna | Zmienna | Często wysokie | Brak opłaty, ale duże ryzyko bezpieczeństwa i operacyjne |
| Proxy datacenter | Zależne od dostawcy i celu | Często szybkie | Zależne od celu | Zwykle rozliczane za GB lub IP |
| Proxy residential | Zależne od dostawcy i celu | Zmienna | Zależne od celu | Zwykle rozliczane za GB |
| Proxy ISP | Zależne od dostawcy i celu | Zmienna | Zależne od celu | Zależne od dostawcy |
Na darmowe proxy warto patrzeć bardzo ostrożnie, bo ryzyko jest realnie spore. 30-miesięczne badanie Free Proxies Unmasked objęło ponad 640 000 adresów od 11 dostawców: 34,5% było aktywnych przynajmniej raz, a 16 923 modyfikowały treść. To wystarczająca podstawa, żeby mocno ostrzegać przed publicznymi listami; nie jest to jednak uniwersalny wskaźnik awaryjności dla każdej aktualnej listy, płatnej puli, celu czy obciążenia.
Proxy też nie gwarantują obejścia nowoczesnych systemów bot-detekcji. Usługi takie jak Cloudflare oceniają requesty na podstawie dziesiątek sygnałów — fingerprintu TLS, spójności nagłówków, wykonania JavaScriptu, wzorców zachowania — a adres IP jest tylko jednym z wielu czynników. Czyste residential proxy przy requestach z niespójnymi nagłówkami i bez cookie jar nadal może zostać oznaczone jako podejrzane. Warto mieć realistyczne oczekiwania, zanim zbudujesz całą architekturę wokół hasła „po prostu rotuj IP”.
Zanim zaczniesz
- Poziom trudności: średni
- Czas potrzebny: około 30–45 minut na pełną konfigurację produkcyjną, około 5 minut na szybki test
- Czego potrzebujesz: Python 3.9+, zainstalowany Scrapy (ten przewodnik testowano na Scrapy 2.17.0, wydanym w lipcu 2026), działający spider utworzony przez
scrapy startprojectoraz przynajmniej jeden endpoint proxy (na potrzeby testu wystarczy darmowy trial od dowolnego dostawcy proxy datacenter)
Krok 1: Przetestuj proxy przez parametr request.meta
Najszybszy sposób sprawdzenia, czy proxy działa, to całkowicie ominąć architekturę middleware i po prostu je przetestować.
Wbudowane HttpProxyMiddleware w Scrapy odczytuje klucz proxy bezpośrednio z request.meta i kieruje przez niego request. Bez zmian w ustawieniach, bez klasy middleware — tylko jeden argument.
import scrapy
class ProxyTestSpider(scrapy.Spider):
name = "proxy_test"
start_urls = ["https://httpbin.org/ip"]
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(
url,
meta={"proxy": "http://203.0.113.10:8080"},
callback=self.parse,
)
def parse(self, response):
self.logger.info(response.text)
Uruchom to poleceniem scrapy runspider proxy_test.py. Jeśli proxy działa, httpbin.org/ip zwróci IP proxy zamiast Twojego — i to będzie potwierdzenie. Jeśli odpowiedź wisi i finalnie kończy się błędem TCP connection timed out, proxy jest martwe albo niedostępne, co — nie będzie zaskoczeniem — zdarza się częściej, niż dostawcy proxy lubią przyznawać.
Ta metoda jest dobra przy jednorazowych spiderach albo szybkich testach. Rozsypuje się jednak wtedy, gdy masz więcej niż jednego spidera, bo nagle wpisujesz ten sam adres proxy na sztywno w pięciu różnych plikach.
Krok 2: Zbuduj własny proxy middleware
Jeśli wykraczasz poza pojedynczego spidera, logika proxy powinna być scentralizowana w jednym miejscu. Utwórz klasę ProxyMiddleware w pliku middlewares.py w swoim projekcie:
from scrapy.exceptions import NotConfigured
class ProxyMiddleware:
def __init__(self, proxy_url):
self.proxy_url = proxy_url
@classmethod
def from_crawler(cls, crawler):
proxy_url = crawler.settings.get("PROXY_URL")
if not proxy_url:
raise NotConfigured("PROXY_URL is required; refusing silent direct fallback")
return cls(proxy_url=proxy_url)
def process_request(self, request, spider):
if "proxy" not in request.meta:
request.meta["proxy"] = self.proxy_url
A potem zarejestruj je w settings.py:
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Zwróć uwagę na metodę klasową from_crawler zamiast zwykłego __init__. To właśnie rekomendowany przez Scrapy sposób czytania ustawień, i ten sam mechanizm przyda nam się ponownie w kroku 4, gdy będziemy mówić o sekretach. Zaletą jest prostota: zmieniasz jedno ustawienie i wszystkie spidery w projekcie przejmują nowe proxy. Koniec z przeszukiwaniem pięciu plików w poszukiwaniu martwego IP.
Krok 3: Zrozum kolejność wykonywania middleware, żeby proxy nie wyglądało na „nic nierobienie”
To właśnie ten fragment większość innych poradników zbywa krótkim „ustaw priorytet na 350 i zaufaj mi”. Downloader middleware w Scrapy działa w konkretnej, przewidywalnej kolejności, a jeśli jej nie rozumiesz, konfiguracja proxy będzie generowała błędy, które wyglądają zupełnie jak coś innego.
Hooki po stronie requestów (process_request) wykonują się w kolejności rosnącej — najniższy numer idzie pierwszy. Hooki po stronie odpowiedzi (process_response, process_exception) działają w kolejności malejącej — najwyższy numer pierwszy, cofając się w dół łańcucha.
Przepływ requestu (rosnąco):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] TWOJE PROXY MIDDLEWARE
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
Przepływ odpowiedzi (malejąco):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] TWOJE PROXY MIDDLEWARE → [300] HttpAuth
→ [100] RobotsTxt → Spider
Oto aktualna tabela domyślnych priorytetów wbudowanych middleware Scrapy (zweryfikowana na 2.17.0):
| Middleware | Domyślny priorytet |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
Gdy RetryMiddleware planuje ponowną próbę, kopiuje nieudany request — razem z jego metadanymi — a ten nowy request ponownie wchodzi do łańcucha downloader middleware. Relacja selektora do priorytetu 550 nie gwarantuje rotacji. Rotacja zachodzi tylko wtedy, gdy kod selektora rozpozna retry i celowo nadpisze skopiowane meta["proxy"]. Priorytet 350 to wygodne miejsce dla selektora, bo działa przed wbudowanym etapem transportu na 750, ale sam w sobie nie jest mechanizmem rotacji.

Typowe błędy w kolejności middleware
- Ustawienie middleware na ten sam priorytet co
HttpProxyMiddleware(750): tworzy to warunek wyścigu, w którym o kolejności przetwarzania decyduje Scrapy (a nie Twoja logika) przez kolejność w słowniku. Objaw: nieregularne, niewyjaśnione zachowanie proxy. - Używanie
setdefault()alboif "proxy" not in request.metaw selektorze rotującym: skopiowany retry zachowuje stare proxy. Objaw: każda ponowna próba idzie tą samą, nieskuteczną trasą. Naprawa: wykryj retry (na przykładretry_times > 0) i jawnie podmień wartość proxy należącą do selektora. - Całkowite wyłączenie
HttpProxyMiddleware: niektóre poradniki sugerują ustawienie"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": None, bo „custom middleware zrobi to samo”. Nie zrobi — Twoje middleware jest selektorem, nie warstwą transportową. Wyłączenie wbudowanego mechanizmu sprawia, że nagłówki autoryzacji proxy nigdy nie są dodawane, a requesty wychodzą bez uwierzytelnienia (albo w ogóle nie wychodzą).
Krok 4: Przestań wpisywać dane uwierzytelniające proxy na sztywno
Każdy poradnik o proxy dla Scrapy, który trafiał wysoko w wynikach, wpisywał http://username:password@proxy.example.com:8080 bezpośrednio do pliku Pythona. To poświadczenie, które zostaje w historii Git na zawsze, pojawia się w każdym klonie, każdym forku i w każdym log dumpie, jeśli ktoś bezmyślnie użyje print().
W praktyce są trzy poziomy takiego podejścia:
| Metoda | Bezpieczeństwo | Elastyczność | Najlepsza do |
|---|---|---|---|
| Na sztywno w spider/settings.py | Słabe — sekrety lądują w repozytorium | Niska | Tylko szybkie testy lokalne |
Zmienna środowiskowa http_proxy (natywnie w Scrapy) | Lepsze — poza kodem | Niska (jedno proxy) | CI/CD, Docker |
Plik .env + python-dotenv + from_crawler | Najlepsze — poza kodem, osobno dla każdego środowiska | Wysoka (wiele proxy, rotacja) | Scrapery produkcyjne |
Trzecia opcja naprawdę warto zbudować porządnie. Zainstaluj python-dotenv, utwórz plik .env i od razu dodaj go do .gitignore — mówię serio, zrób to teraz:
PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080
Załaduj to na początku settings.py:
from dotenv import load_dotenv
import os
load_dotenv()
PROXY_USER = os.getenv("PROXY_USER")
PROXY_PASSWORD = os.getenv("PROXY_PASSWORD")
PROXY_HOST = os.getenv("PROXY_HOST")
PROXY_PORT = os.getenv("PROXY_PORT")
Następnie odczytaj te dane bezpiecznie wewnątrz middleware, używając from_crawler — to właśnie ten wzorzec rozwiązuje zamieszanie, które często widuję na forach, gdzie ludzie pytają: „jak to ustawić przed scrapy crawl, jeśli dane logowania zmieniają się przy każdym uruchomieniu?”:
import os
from urllib.parse import quote
class SecureProxyMiddleware:
def __init__(self, user, password, host, port):
self.user = quote(user, safe="")
self.password = quote(password, safe="")
self.host = host
self.port = port
@classmethod
def from_crawler(cls, crawler):
settings = crawler.settings
return cls(
user=settings.get("PROXY_USER"),
password=settings.get("PROXY_PASSWORD"),
host=settings.get("PROXY_HOST"),
port=settings.get("PROXY_PORT"),
)
def process_request(self, request, spider):
proxy_url = f"http://{self.user}:{self.password}@{self.host}:{self.port}"
request.meta["proxy"] = proxy_url
Zwróć uwagę na urllib.parse.quote() zastosowane do danych logowania. Jeśli hasło zawiera @, : albo / (a generatory haseł uwielbiają takie znaki), URL zostanie źle zinterpretowany, chyba że wcześniej je zakodujesz procentowo. To jedno linijkowe zabezpieczenie oszczędza żenującej godziny debugowania błędów typu „invalid proxy URL”, które wcale nie wynikają z tego, że proxy jest wadliwe.
Trzymaj dane logowania poza logami i testuj kodowanie procentowe na reprezentatywnych — ale fikcyjnych — wartościach. Tunneling HTTPS, SOCKS5 i poświadczenia z niełacińskimi znakami mają ograniczenia zależne od handlera; nie zakładaj, że jeden schemat uwierzytelniania zadziała wszędzie bez przypiętego testu integracyjnego.

Krok 5: Dodaj rotację proxy
Jedno proxy — nawet dobre — wykonujące 5 000 requestów do tej samej strony w końcu zostanie oznaczone. Potrzebujesz puli.
Opcja A — zbuduj to samodzielnie. To naprawdę proste:
import random
class RotatingProxyMiddleware:
def __init__(self, proxy_pool):
self.proxy_pool = proxy_pool
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def process_request(self, request, spider):
request.meta["proxy"] = random.choice(self.proxy_pool)
To działa przy prostych zastosowaniach, ale nie wie nic o tym, które proxy faktycznie żyją. Przy każdym requestcie rzucasz kością.
Opcja B — użyj scrapy-rotating-proxies. Ten pakiet zewnętrzny dodaje wykrywanie banów i automatyczny backoff od razu po instalacji:
pip install scrapy-rotating-proxies
Powiem to uczciwie: najnowsza wersja na PyPI to 0.6.2 z 2019 roku, a projekt ma etykietę Alpha. Niekoniecznie oznacza to, że nie działa na nowym Scrapy, ale „nieutrzymywany aktywnie od 2019” to nie to samo co „przetestowany bojowo na ruchu produkcyjnym w 2026”. Przypnij wersję, przetestuj ją na realnych celach i nie zakładaj, że obsługuje autoryzowane endpointy proxy — w dużej mierze tego nie robi.
Krok 6: Zbuduj odporny na awarie proxy middleware (wykrywanie martwych proxy)
To jest sekcja, którą każdy konkurencyjny poradnik pomija całkowicie, a właśnie ona odróżnia demo od rozwiązania, które przetrwa 6-godzinny crawl bez nadzoru.
| Scenariusz | Co pokazuje większość poradników | Co dodaje to middleware |
|---|---|---|
| Proxy zwraca 407 | Brak omówienia | Traktuje to jako błąd autoryzacji proxy |
| Cel zwraca 403/429 | Często wrzucane do jednego worka | Oddziela sygnały polityki/limitów od kondycji proxy |
| Proxy timeoutuje | Brak omówienia | Konfigurowalny próg timeoutu, spadek score kondycji |
| Wszystkie proxy martwe | Brak omówienia | Łagodny fallback albo pauza crawl'a z ostrzeżeniem w logu |
| Proxy „flapping” (przerywane działanie) | Brak omówienia | Okres schłodzenia przed ponownym dodaniem do puli |
import time
import random
from scrapy.exceptions import IgnoreRequest
class FaultTolerantProxyMiddleware:
MAX_FAILURES = 3
COOLDOWN_SECONDS = 300
def __init__(self, proxy_pool):
self.pool = {p: {"failures": 0, "banned_until": 0} for p in proxy_pool}
@classmethod
def from_crawler(cls, crawler):
return cls(proxy_pool=crawler.settings.getlist("PROXY_POOL"))
def _healthy_proxies(self):
now = time.time()
return [p for p, s in self.pool.items() if s["banned_until"] < now]
def process_request(self, request, spider):
healthy = self._healthy_proxies()
if not healthy:
spider.logger.warning("Wszystkie proxy są niezdrowe — wstrzymuję crawl")
raise IgnoreRequest("No healthy proxies available")
request.meta["proxy"] = random.choice(healthy)
def process_response(self, request, response, spider):
proxy = request.meta.get("proxy")
if proxy and response.status == 407:
self._mark_failure(proxy)
return response
def process_exception(self, request, exception, spider):
proxy = request.meta.get("proxy")
if proxy:
self._mark_failure(proxy)
def _mark_failure(self, proxy):
state = self.pool[proxy]
state["failures"] += 1
if state["failures"] >= self.MAX_FAILURES:
state["banned_until"] = time.time() + self.COOLDOWN_SECONDS
state["failures"] = 0
Kilka uwag z praktyki: nie traktuj każdego 403 jako dowodu, że proxy jest martwe — RFC 9110 definiuje 403 jako „serwer zrozumiał, ale odmawia”, co równie dobrze może oznaczać, że podejrzane są nagłówki albo sesja, a nie samo proxy. Z kolei 407 oznacza, że to samo proxy odrzuca autoryzację — to silniejszy i bardziej konkretny sygnał. Mieszanie tych sygnałów w jeden koszyk sprawia, że ludzie spalają zdrowe proxy bez powodu.
W produkcji warto jawnie ustawić tymczasowe retry w Scrapy, żeby recenzenci widzieli politykę:
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2
To są domyślne wartości Scrapy 2.17. Nie dodawaj masowo 403 ani 407: 403 to odmowa po stronie celu z wielu możliwych powodów, a 407 to problem z autoryzacją proxy, którego zwykłe ponawianie requestu nie naprawi. Jeśli konkretny kontrakt celu uzasadnia retry dla innego kodu, udokumentuj ten powód i przetestuj go osobno.

Krok 7: Mierzony wzorzec proxy przy retry
Niektóre autoryzowane cele mogą zwracać poprawną treść bezpośrednio i potrzebować proxy dopiero po udokumentowanej, przejściowej odpowiedzi. Może to zmniejszyć liczbę bajtów przesyłanych przez proxy, ale nie istnieje obronny, uniwersalny procent oszczędności ani przenośny współczynnik sukcesu bezpośrednich requestów. Zmierz własne obciążenie, zanim przyjmiesz ten wzorzec.
Użyj publicznego helpera Scrapy get_retry_request() i ogranicz eskalację wyłącznie do specyficznych dla celu statusów, które zostały przez Ciebie jawnie sklasyfikowane. Ten przykład traktuje 429 i 503 jako sygnały przeciążenia kwalifikujące do retry; celowo wyklucza 403 i 407.
from scrapy.downloadermiddlewares.retry import get_retry_request
class CostAwareEscalationMiddleware:
def process_response(self, request, response, spider):
if response.status not in {429, 503}:
return response
retry = get_retry_request(
request,
spider=spider,
reason=f"proxy_escalation_{response.status}",
max_retry_times=2,
)
if retry is None:
return response
current_tier = request.meta.get("proxy_tier", "direct")
if current_tier == "direct":
retry.meta["proxy"] = spider.settings["DATACENTER_PROXY"]
retry.meta["proxy_tier"] = "datacenter"
elif current_tier == "datacenter":
retry.meta["proxy"] = spider.settings["RESIDENTIAL_PROXY"]
retry.meta["proxy_tier"] = "residential"
else:
return response
return retry
Loguj współczynnik poprawnej treści przy direct, współczynnik poprawnej treści przez proxy, liczbę bajtów na rekord zakończony sukcesem, liczbę retry na sukces i dodatkowe opóźnienie. Architektura direct-first jest akceptowalna tylko wtedy, gdy direct access jest autoryzowany, a system przechodzi w tryb fail-closed zawsze, gdy proxy jest wymagane. Oszczędność to zmierzona redukcja ruchu przez proxy — nie założony procent.
Kiedy samodzielne zarządzanie proxy nie ma sensu
Chcę powiedzieć to wprost, zamiast udawać, że cała reszta artykułu była bez sensu: wszystko powyżej to prawdziwa, użyteczna inżynieria i w wielu projektach — crawlach o dużym wolumenie, niestandardowych pipeline’ach, wszędzie tam, gdzie potrzebujesz pełnej kontroli nad harmonogramem requestów — jest to właściwy wybór.
Ale jeśli Twoim realnym celem jest „pobierz ustrukturyzowane dane z tej strony”, a nie „utrzymuj infrastrukturę proxy”, API ekstrakcyjne może być lepszą granicą odpowiedzialności. Aktualna dokumentacja Thunderbit dla POST /extract przyjmuje URL strony i opcjonalny JSON Schema; gdy schemat jest pominięty, usługa może wygenerować go na podstawie treści strony. Endpoint udostępnia też tryby renderowania none, basic i full, a także kontrolę timeoutu i czasu oczekiwania po załadowaniu strony. Ekstrakcja wyłącznie na podstawie promptu nie jest częścią aktualnie wspieranego zakresu requestu, więc integracje produkcyjne buduj wokół udokumentowanego kontraktu schematu. To przenosi interfejs ekstrakcji za jeden request; nie uzasadnia jednak uniwersalnej obietnicy skuteczności wobec każdego anty-botowego mechanizmu czy CAPTCHA.
| Czynnik | DIY Scrapy + proxy | Ekstrakcja oparta o API (np. Thunderbit) |
|---|---|---|
| Nakład startowy | Wysoki — middleware, rotacja, logika retry | Niski — jedno wywołanie API ze schematem |
| Zachowanie sieci/renderowania | Konfigurujesz handlery, proxy, nagłówki i opóźnienia | Sterowane przez udokumentowane opcje API |
| Utrzymanie | Odpowiadasz za selektory, kondycję puli i zmiany celu | Odpowiadasz za jakość schematu, walidację i integrację |
| Model kosztów | Opłaty za proxy + compute + czas inżynierów | Aktualna dokumentacja podaje 20 jednostek na stronę (sprawdzone 2026-08-10) |
| Kontrola | Pełna — własne pipeline’y, łańcuch middleware | Ograniczona do możliwości API |
| Najlepsze dla | Złożone crawl'e, duży wolumen, własna logika | Ukierunkowana ekstrakcja, prototypowanie, enrichment |
Jeśli głównie wyciągasz ustrukturyzowane strony do list leadów, danych produktowych albo researchu, uruchom reprezentatywny pilot z Thunderbit Chrome Extension albo API i porównaj liczbę poprawnych rekordów, opóźnienie, jednostki oraz czas utrzymania. Jeśli Twój projekt wymaga własnych grafów crawl'i i kontroli pipeline’u, Scrapy pozostaje lepszym wyborem.
Dowiedz się więcej
Wskazówki i częste pułapki
- Wskazówka: Przetestuj proxy na
httpbin.org/ip, zanim podłączysz je do prawdziwego celu. To najszybszy sposób, żeby sprawdzić, czy routing działa, zanim dołożysz kolejne warstwy złożoności. - Pułapka: Ustawianie proxy w
request.headerszamiast wrequest.meta. To naprawdę częsty, niemal literówkowy błąd —HttpProxyMiddlewareodczytuje tylkometa["proxy"], a próba oparta na nagłówkach zakończy się cicho, bez oczywistego błędu. - Pułapka: Zakładanie, że proxy HTTP i HTTPS konfiguruje się identycznie. Routing przez HTTP proxy do celu HTTPS zwykle działa przez tunel CONNECT przy kompatybilnym handlerze, ale użycie schematu
https://dla samego endpointu proxy to zupełnie inna, słabiej wspierana konfiguracja — nie myl tych dwóch rzeczy. - Wskazówka: Jeśli potrzebujesz wsparcia dla SOCKS5, najpierw sprawdź możliwości download handlera w swojej wersji Scrapy. Eksperymentalny
Httpxhandler w Scrapy 2.17 dodał obsługę SOCKS5 przezhttpx[socks], ale nadal jest oznaczony jako eksperymentalny — nie opieraj na nim produkcji bez własnych testów.
Metody alternatywne
Poza scrapy-rotating-proxies niektóre zespoły kierują całą logikę proxy przez gateway dostawcy — pojedynczy URL proxy, za którym dostawca sam obsługuje rotację, sticky session i targetowanie geograficzne. To oddaje część kontroli w zamian za znacznie mniej kodu middleware, i warto wycenić to rozwiązanie względem własnej puli, zanim zaczniesz budować wszystko od zera.
Podsumowanie
Ustawienie proxy w Scrapy wymaga jednej linii. Sprawienie, by taka konfiguracja przetrwała prawdziwy produkcyjny crawl, wymaga jawnej polityki błędów, zabezpieczonych danych uwierzytelniających, przetestowanych granic handlerów oraz kodu selektora, który świadomie podmienia skopiowane metadane proxy przy retry. Jeśli masz wynieść z tego dwie nawyki, niech będą to: fail closed, gdy proxy jest wymagane, oraz nigdy nie commituj hasła do proxy do pliku Pythona.
FAQ
Jak skonfigurować własny proxy w Scrapy z uwierzytelnianiem?
Użyj formatu URL protocol://username:password@host:port, ale najpierw zakoduj username i password przy pomocy urllib.parse.quote(), jeśli zawierają znaki specjalne. W produkcji czytaj te dane przez metodę klasową from_crawler, pobierając je ze zmiennych środowiskowych zamiast wpisywać je na sztywno.
Jakiego numeru priorytetu użyć dla własnego proxy middleware w Scrapy?
350 to częsty wybór dla selektora, ponieważ działa przed HttpProxyMiddleware na 750. Nie gwarantuje to rotacji. Retry dostaje nowe proxy tylko wtedy, gdy selektor rozpozna skopiowany request retry i nadpisze jego wcześniejszą wartość meta["proxy"].
Jak automatycznie obsługiwać martwe proxy w Scrapy?
Zbuduj middleware, które śledzi liczbę błędów per proxy w process_response i process_exception, usuwa proxy z aktywnej puli po przekroczeniu progu błędów i dodaje je ponownie po okresie schłodzenia, zamiast blokować je na stałe.
Czy mogę używać Scrapy z proxy SOCKS5?
Eksperymentalny HttpxDownloadHandler w Scrapy 2.17 dokumentuje wsparcie dla SOCKS5, jeśli zainstalowano httpx[socks]. Domyślny handler HTTP/1.1 nie obsługuje proxy SOCKS. Zablokuj wersję handlera i zrób test integracyjny, zanim uznasz tę ścieżkę za gotową do produkcji.
Ile naprawdę można zaoszczędzić na kosztach proxy dzięki wzorcowi proxy-on-retry? Nie istnieje uniwersalny procent. Zmierz udział autoryzowanych requestów, które zwracają poprawną treść bezpośrednio, liczbę bajtów przesyłanych przez każdą warstwę proxy, liczbę retry na każdy rekord zakończony sukcesem i dodatkowe opóźnienie. Zaoszczędzona kwota to obserwowana redukcja ruchu przez proxy — jeśli direct-first nie jest autoryzowane albo nie działa poprawnie, nie stosuj tego wzorca.


