Scrapy-spiderisi pyyhkii testissä läpi 100 sivua, mutta hajoaa 10 000 sivun kohdalla. Se ei oikeastaan ole kaavintavirhe — vaan verkkoyhteyksien ongelma, joka on naamioitunut kaavintavirheeksi. Yleisin ratkaisu on proxy, ja sen lisääminen request.meta-kenttään vie noin kolmekymmentä sekuntia.
Ongelma on siinä, että juuri siihen 30 sekunnin korjaukseen monet perusoppaat myös loppuvat. Ne näyttävät meta={"proxy": "http://IP:PORT"}-esimerkin, ehkä middleware-luokan, ja sitten muka kaikki on selvää. Usein jätetään kertomatta, mitä tapahtuu kun proxy kaatuu kesken kaavinnan, miten tunnukset päätyvät Git-historiaan, tai miksi retry saattaa käyttää uudelleen samaa epäonnistunutta proxya. Tässä oppaassa käydään läpi juuri nämä tuotannon kannalta tärkeät asiat: virheissä sulkeva konfigurointi, salaisuuksien hallinta, harkittu proxyn valinta retry-tilanteissa, protokollarajat ja mitattavat kustannuserot.
Mikä on proxy middleware Scrapyssa?
Proxy middleware on koodipala, joka sijaitsee Scrapyn downloader-putkessa ja päättää, mistä IP-osoitteesta pyyntö näyttää tulevan ennen kuin se lähtee verkkoon. Scrapyssa on valmiina HttpProxyMiddleware, joka lukee proxy-avaimen request.meta-kentästä, lisää tarvittaessa proxy-tunnistautumisen ja välittää pyynnön download handlerille, joka oikeasti avaa yhteyden. Oma proxy middleware ei korvaa tätä siirtovaihetta — se on valitsin, joka päättää, mikä proxy asetetaan ennen kuin sisäänrakennettu mekanismi hoitaa varsinaisen kuljetuksen. Tällä erolla on enemmän merkitystä kuin ensi silmäyksellä vaikuttaa, ja se on taustalla monessa "miksi oma middleware ei toimi" -ongelmassa.
Miksi proxyt ovat tärkeitä tuotantotason Scrapy-projekteissa
Proxy muuttaa verkkoreitin ja näkyvän IP-osoitteen. Siitä voi olla hyötyä esimerkiksi maakohtaisessa testauksessa, valtuutetun liikenteen jakamisessa ja verkkohäiriöiden eristämisessä. Se ei kuitenkaan anna lupaa, ohita rate limit -rajoja tai takaa pääsyä. Tarvitaanko proxya ylipäätään, riippuu kohteesta, käyttöehdoista, pyyntötiheydestä ja työn luotettavuusvaatimuksista.
Kaikki proxyt eivät ole samanlaisia, ja väärän tason valinta voi olla aivan oma tuotanto-ongelmansa:
| Proxy-tyyppi | Luotettavuus | Nopeus | Havaitsemisriski | Tyypillinen hinta |
|---|---|---|---|---|
| Ilmaiset julkiset proxyt | Erittäin vaihteleva | Vaihteleva | Usein korkea | Ei maksua, mutta merkittävä tietoturva- ja käyttöuhkariski |
| Datacenter-proxyt | Palveluntarjoaja- ja kohderiippuvainen | Usein nopea | Kohderiippuvainen | Yleensä hinnoiteltu GB:n tai IP:n mukaan |
| Residential-proxyt | Palveluntarjoaja- ja kohderiippuvainen | Vaihteleva | Kohderiippuvainen | Yleensä hinnoiteltu GB:n mukaan |
| ISP-proxyt | Palveluntarjoaja- ja kohderiippuvainen | Vaihteleva | Kohderiippuvainen | Palveluntarjoajakohtainen |
Ilmaiset proxyt ansaitsevat erityismaininnan, koska mitatut riskit ovat huomattavia. 30 kuukauden mittainen Free Proxies Unmasked -tutkimus seurasi yli 640 000 osoitetta 11 tarjoajalta: 34,5 % oli aktiivisia ainakin kerran, ja 16 923 manipuloi sisältöä. Tuo aineisto antaa vahvan tietoturvavaroituksen julkisille listauksille; se ei ole suoraan sovellettava virheprosentti jokaiseen nykyiseen listaan, maksulliseen pooliin, kohteeseen tai työkuormaan.
Proxyt eivät myöskään taianomaisesti kierrä nykyaikaista bottitunnistusta. Palvelut kuten Cloudflare pisteyttävät pyyntöjä kymmenillä signaaleilla — TLS-sormenjäljillä, otsaketietojen johdonmukaisuudella, JavaScriptin suorittamisella ja käyttäytymismalleilla — jolloin IP-osoite on vain yksi signaali muiden joukossa. Puhdas residential-proxy ei auta, jos pyynnössä on ristiriitaiset headerit eikä cookie jar -tilaa ole lainkaan. Pidä tämä mielessä ennen kuin rakennat koko arkkitehtuurin ajatuksella "vaihdetaan vain IP".
Ennen kuin aloitat
- Vaikeustaso: Keskitaso
- Aikaa tarvitaan: noin 30–45 minuuttia koko tuotantokäyttöön valmiiseen toteutukseen, noin 5 minuuttia nopeaan testiin
- Tarvitset: Python 3.9+, Scrapy asennettuna (tämä opas on testattu versiolla Scrapy 2.17.0, joka julkaistiin heinäkuussa 2026), toimivan spiderin
scrapy startproject-komennolla luodusta projektista sekä vähintään yhden proxy-pisteen (ilmainen kokeiluversio miltä tahansa datacenter-proxytoimittajalta käy testaukseen)
Vaihe 1: Testaa proxy request.meta-parametrilla
Nopein tapa varmistaa, että proxy toimii, on ohittaa koko middleware-arkkitehtuuri ja testata sitä suoraan.
Scrapyn sisäänrakennettu HttpProxyMiddleware lukee proxy-avaimen suoraan request.meta-kentästä ja reitittää pyynnön sen kautta. Ei asetusten säätöä, ei middleware-luokkaa — vain yksi avainarvo.
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)
Aja se komennolla scrapy runspider proxy_test.py. Jos proxy toimii, httpbin.org/ip palauttaa oman IP:si sijaan proxyn IP-osoitteen — se on vahvistus. Jos vastaus jumittuu ja päätyy lopulta TCP connection timed out -virheeseen, proxy on kuollut tai tavoittamattomissa, mikä, paljastetaanpa tämä nyt, tapahtuu useammin kuin proxy-toimittajat mielellään myöntävät.
Tämä tapa toimii kertaluontoisissa spiidereissä tai nopeissa testeissä. Se alkaa hajota heti, kun spiidereitä on useampi kuin yksi, koska silloin sama proxy on kovakoodattu viiteen eri tiedostoon.
Vaihe 2: Rakenna oma proxy middleware
Kaikkea muuta kuin yhden spiderin tapauksessa proxy-logiikka kannattaa keskittää yhteen paikkaan. Luo projektisi middlewares.py-tiedostoon ProxyMiddleware-luokka:
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
Ja rekisteröi se settings.py-tiedostossa:
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Huomaa from_crawler-luokkametodi tavallisen __init__-alustajan sijaan. Tämä on Scrapyn suosittelema tapa lukea asetuksia, ja samaa mekanismia hyödynnämme uudelleen vaiheessa 4, kun käsittelemme salaisuuksia. Hyöty on yksinkertainen: vaihda yksi asetus, ja koko projekti ottaa uuden proxyn käyttöön. Ei enää viiden tiedoston läpikäyntiä kuolleen IP-osoitteen vaihtamiseksi.
Vaihe 3: Ymmärrä middleware-ajojärjestys, jotta proxysi ei tee hiljaa mitään
Tässä on se kohta, jonka lähes kaikki muut oppaat ohittavat lauseella "laita prioriteetiksi 350, kyllä se toimii". Scrapyn downloader middleware -ketju toimii tarkassa ja ennustettavassa järjestyksessä, ja jos et ymmärrä sitä, proxy-asetuksesi synnyttää vikoja, jotka näyttävät aivan muilta kuin proxyyn liittyviltä.
Request-puolen hookit (process_request) suoritetaan nousevassa prioriteettijärjestyksessä — pienin numero ensin. Response-puolen hookit (process_response, process_exception) ajetaan laskevassa prioriteettijärjestyksessä — suurin numero ensin, ja ketju purkautuu takaisin alas.
Request flow (ascending):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] YOUR PROXY MIDDLEWARE
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
Response flow (descending):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] YOUR PROXY MIDDLEWARE → [300] HttpAuth
→ [100] RobotsTxt → Spider
Tässä on Scrapyn sisäänrakennettujen middleware-komponenttien todellinen nykyinen oletusprioriteettitaulukko (vahvistettu versiolla 2.17.0):
| Middleware | Oletusprioriteetti |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
Kun RetryMiddleware aikatauluttaa retry-kierroksen, se kopioi epäonnistuneen pyynnön — mukaan lukien sen metadatan — ja uusi pyyntö palaa downloader middleware -ketjuun. Valitsimen ja prioriteetin 550 välinen numeerinen suhde ei takaa rotaatiota. Rotaatiota tapahtuu vain, jos valitsinkoodi tunnistaa retry-pyynnön ja korvaa tietoisesti kopioidun meta["proxy"]-arvon. Prioriteetti 350 on kätevä paikka valitsimelle, koska se suoritetaan ennen sisäänrakennettua kuljetusvaihetta 750:ssa, mutta se ei itsessään ole rotaatiomekanismi.

Yleiset middleware-järjestyksen virheet
- Middlewaren asettaminen samaan prioriteettiin kuin
HttpProxyMiddleware(750): Tämä luo kilpailutilanteen, jossa Scrapyn sanakirja-järjestys (eikä sinun logiikkasi) päättää, mikä middleware käsittelee pyynnön ensin. Oire: satunnainen ja selittämätön proxyn käyttäytyminen. setdefault()- taiif "proxy" not in request.meta-logiikan käyttö rotaatiovalitsimessa: retry-kopio säilyttää vanhan proxyn. Oire: jokainen retry käyttää samaa epäonnistunutta reittiä. Korjaus: tunnista retry (esimerkiksiretry_times > 0) ja korvaa valitsimen omistama proxy-arvo eksplisiittisesti.HttpProxyMiddleware-middlewaren poistaminen kokonaan käytöstä: Jotkin oppaat neuvovat asettamaan `


