Το Scrapy spider σου «σπάει» σε 100 δοκιμαστικές σελίδες και μετά διαλύεται στις 10.000. Αυτό δεν είναι ακριβώς bug του scraping — είναι πρόβλημα δικτύωσης που κάνει πως είναι bug scraping. Η λύση που χρησιμοποιούν όλοι είναι ένα proxy, και το να το βάλεις στο request.meta παίρνει περίπου τριάντα δευτερόλεπτα.
Το πρόβλημα είναι ότι αυτή η λύση των τριάντα δευτερολέπτων είναι και το σημείο όπου σταματούν πολλά βασικά tutorials. Σου δείχνουν meta={"proxy": "http://IP:PORT"}, ίσως μια middleware class, και το κλείνουν εκεί. Συνήθως αφήνουν απ’ έξω το τι γίνεται όταν το proxy πέσει στη μέση του crawl, πώς τα διαπιστευτήρια καταλήγουν στο Git history, ή γιατί ένα retry μπορεί να ξαναχρησιμοποιήσει το ίδιο αποτυχημένο proxy. Αυτές είναι οι ανησυχίες παραγωγής που καλύπτει αυτός ο οδηγός: fail-closed ρυθμίσεις, διαχείριση μυστικών, σκόπιμη επιλογή proxy στο retry, περιορισμοί πρωτοκόλλου και μετρήσιμες συμβιβαστικές επιλογές κόστους.
Τι είναι ένα Proxy Middleware στο Scrapy;
Το proxy middleware είναι ένα κομμάτι κώδικα που κάθονται μέσα στο downloader pipeline του Scrapy και αποφασίζει από ποια IP θα φαίνεται ότι προέρχεται ένα request πριν σταλεί στο δίκτυο. Το Scrapy συνοδεύεται από ένα ενσωματωμένο middleware, το HttpProxyMiddleware, το οποίο διαβάζει ένα κλειδί proxy από το request.meta, προσθέτει proxy authentication αν χρειάζεται και παραδίδει το request στο download handler που ανοίγει πραγματικά τη σύνδεση. Ένα custom proxy middleware δεν αντικαθιστά αυτό το transport βήμα — είναι ένας selector που αποφασίζει ποιο proxy θα μπει πριν αναλάβει ο built-in μηχανισμός. Αυτή η διάκριση έχει πολύ μεγαλύτερη σημασία απ’ ό,τι φαίνεται, και από εκεί προκύπτουν τα περισσότερα bugs τύπου «το custom middleware μου δεν δουλεύει».
Γιατί τα Proxies έχουν σημασία σε Production Scrapy Projects
Ένα proxy αλλάζει τη διαδρομή του δικτύου και την εμφανή IP προέλευση. Αυτό μπορεί να βοηθήσει σε νόμιμο geo-specific testing, να μοιράσει εξουσιοδοτημένη κίνηση αιτημάτων και να απομονώσει προβλήματα δικτύου. Δεν δίνει άδεια, δεν παρακάμπτει rate limits και δεν εγγυάται πρόσβαση. Το αν ένα crawl χρειάζεται καθόλου proxy εξαρτάται από τον στόχο, τους όρους χρήσης του, τον ρυθμό αιτημάτων και τις απαιτήσεις αξιοπιστίας της δουλειάς.
Δεν είναι όλα τα proxies ίδια, και η λάθος επιλογή tier μπορεί από μόνη της να γίνει production incident:
| Τύπος Proxy | Αξιοπιστία | Ταχύτητα | Κίνδυνος ανίχνευσης | Συνήθες Κόστος |
|---|---|---|---|---|
| Δωρεάν δημόσια proxies | Εξαιρετικά μεταβλητή | Μεταβλητή | Συχνά υψηλός | Χωρίς χρέωση, αλλά με ουσιαστικό κίνδυνο ασφάλειας/λειτουργίας |
| Datacenter proxies | Εξαρτάται από πάροχο και στόχο | Συχνά γρήγορα | Εξαρτάται από τον στόχο | Συνήθως ανά GB ή ανά IP |
| Residential proxies | Εξαρτάται από πάροχο και στόχο | Μεταβλητή | Εξαρτάται από τον στόχο | Συνήθως ανά GB |
| ISP proxies | Εξαρτάται από πάροχο και στόχο | Μεταβλητή | Εξαρτάται από τον στόχο | Ανάλογα με τον πάροχο |
Τα δωρεάν proxies αξίζουν ειδική αναφορά, γιατί οι μετρημένοι κίνδυνοί τους είναι σημαντικοί. Η 30μηνη μελέτη Free Proxies Unmasked παρακολούθησε πάνω από 640.000 διευθύνσεις από 11 παρόχους: το 34,5% ήταν ενεργό τουλάχιστον μία φορά και 16.923 τροποποίησαν περιεχόμενο. Αυτό το δείγμα στηρίζει μια δυνατή προειδοποίηση ασφάλειας για δημόσιες λίστες· δεν είναι όμως μεταφέρσιμο ποσοστό αποτυχίας για κάθε τρέχουσα λίστα, πληρωμένη δεξαμενή, στόχο ή φόρτο εργασίας.
Τα proxies επίσης δεν νικάνε μαγικά τον σύγχρονο bot detection. Υπηρεσίες όπως το Cloudflare βαθμολογούν τα requests με δεκάδες σήματα — TLS fingerprints, συνέπεια headers, εκτέλεση JavaScript, συμπεριφορικά μοτίβα — όπου η IP είναι μόνο μία είσοδος ανάμεσα σε πολλές. Ένα καθαρό residential proxy πάνω σε request με ασυνεπή headers και χωρίς cookie jar θα επισημανθεί κι αυτό. Κράτα αυτή την προσδοκία υπό έλεγχο πριν χτίσεις ολόκληρη αρχιτεκτονική γύρω από το «απλώς κάνουμε rotate την IP».
Πριν Ξεκινήσετε
- Δυσκολία: Μεσαία
- Χρόνος που απαιτείται: ~30–45 λεπτά για πλήρη ρύθμιση παραγωγής, ~5 λεπτά για το γρήγορο τεστ
- Τι θα χρειαστείτε: Python 3.9+, εγκατεστημένο Scrapy (ο οδηγός έχει δοκιμαστεί με Scrapy 2.17.0, που κυκλοφόρησε τον Ιούλιο του 2026), ένα λειτουργικό spider από
scrapy startprojectκαι τουλάχιστον ένα endpoint proxy (ένα free trial από οποιονδήποτε πάροχο datacenter proxy είναι αρκετό για δοκιμές)
Βήμα 1: Δοκιμάστε ένα Proxy με την Παράμετρο Request Meta
Ο πιο γρήγορος τρόπος να επιβεβαιώσεις ότι ένα proxy δουλεύει είναι να παρακάμψεις τελείως όλη την αρχιτεκτονική middleware και να το δοκιμάσεις απευθείας.
Το ενσωματωμένο HttpProxyMiddleware του Scrapy διαβάζει το κλειδί proxy κατευθείαν από το request.meta και δρομολογεί το request μέσω αυτού. Χωρίς αλλαγές στα settings, χωρίς middleware class — μόνο με ένα keyword 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)
Τρέξε το με scrapy runspider proxy_test.py. Αν το proxy δουλεύει, το httpbin.org/ip θα επιστρέψει την IP του proxy αντί για τη δική σου — αυτή είναι η επιβεβαίωση. Αν η απόκριση κολλήσει και τελικά πετάξει σφάλμα TCP connection timed out, το proxy έχει πεθάνει ή δεν είναι προσβάσιμο, κάτι που, ας μην κοροϊδευόμαστε, συμβαίνει πιο συχνά απ’ όσο παραδέχονται οι πάροχοι proxy.
Αυτή η μέθοδος είναι μια χαρά για one-off spiders ή γρήγορα tests. Καταρρέει τη στιγμή που έχεις περισσότερα από ένα spider, γιατί τότε γράφεις το ίδιο proxy string σε πέντε διαφορετικά αρχεία.
Βήμα 2: Φτιάξτε ένα Custom Proxy Middleware
Για οτιδήποτε πέρα από ένα μόνο spider, θέλεις η λογική του proxy να συγκεντρώνεται σε ένα σημείο. Δημιούργησε μια κλάση ProxyMiddleware στο middlewares.py του project σου:
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 είναι υποχρεωτικό· δεν επιτρέπεται σιωπηρή επιστροφή σε direct connection")
return cls(proxy_url=proxy_url)
def process_request(self, request, spider):
if "proxy" not in request.meta:
request.meta["proxy"] = self.proxy_url
Και δήλωσέ το στο settings.py:
import os
PROXY_URL = os.environ.get("PROXY_URL")
DOWNLOADER_MIDDLEWARES = {
"myproject.middlewares.ProxyMiddleware": 350,
}
Πρόσεξε τη μέθοδο from_crawler αντί για ένα απλό __init__. Αυτό είναι το μοτίβο που προτείνει πραγματικά το Scrapy για ανάγνωση settings, και είναι το ίδιο hook που θα αξιοποιήσουμε ξανά στο Βήμα 4 όταν μιλήσουμε για secrets. Το πλεονέκτημα εδώ είναι απλό: αλλάζεις μία ρύθμιση και όλα τα spiders του project παίρνουν το νέο proxy. Τέλος το ψάξιμο σε πέντε αρχεία για να αλλάξεις μια νεκρή IP.
Βήμα 3: Κατανοήστε τη Σειρά Εκτέλεσης των Middleware (Για να Μην Κάνει Το Proxy Σας Σιωπηλά Τίποτα)
Εδώ είναι το σημείο που σχεδόν όλα τα άλλα tutorials το προσπερνούν με ένα «βάλε απλώς priority 350, εμπιστεύσου με». Το downloader middleware στο Scrapy εκτελείται με συγκεκριμένη, προβλέψιμη σειρά, και αν δεν την καταλάβεις, η ρύθμιση του proxy σου θα βγάζει bugs που μοιάζουν τελείως άσχετα με τα proxies.
Τα request-side hooks (process_request) τρέχουν σε αύξουσα σειρά priority — πρώτα ο μικρότερος αριθμός. Τα response-side hooks (process_response, process_exception) τρέχουν σε φθίνουσα σειρά — πρώτα ο μεγαλύτερος αριθμός, και μετά ξετυλίγονται πίσω στην αλυσίδα.
Ροή Request (αύξουσα):
Spider → [100] RobotsTxt → [300] HttpAuth → [350] ΤΟ ΔΙΚΟ ΣΑΣ PROXY MIDDLEWARE
→ [500] UserAgent → [550] Retry → [700] Cookies → [750] HttpProxy
→ Downloader
Ροή Response (φθίνουσα):
Downloader → [750] HttpProxy → [700] Cookies → [550] Retry
→ [500] UserAgent → [350] ΤΟ ΔΙΚΟ ΣΑΣ PROXY MIDDLEWARE → [300] HttpAuth
→ [100] RobotsTxt → Spider
Ακολουθεί ο πραγματικός, τρέχων πίνακας default priorities για τα built-in middlewares του Scrapy (επιβεβαιωμένος με το 2.17.0):
| Middleware | Default Priority |
|---|---|
| RobotsTxtMiddleware | 100 |
| HttpAuthMiddleware | 300 |
| DownloadTimeoutMiddleware | 350 |
| DefaultHeadersMiddleware | 400 |
| UserAgentMiddleware | 500 |
| RetryMiddleware | 550 |
| RedirectMiddleware | 600 |
| CookiesMiddleware | 700 |
| HttpProxyMiddleware | 750 |
| DownloaderStats | 850 |
| HttpCacheMiddleware | 900 |
Όταν το RetryMiddleware προγραμματίζει ένα retry, αντιγράφει το αποτυχημένο request — μαζί με τα metadata του — και αυτό το νέο request μπαίνει ξανά στην αλυσίδα downloader middleware. Η αριθμητική σχέση του selector με το priority 550 δεν εγγυάται καθόλου rotation. Η rotation συμβαίνει μόνο όταν ο κώδικας του selector αναγνωρίζει το retry και αντικαθιστά σκόπιμα το αντιγραμμένο meta["proxy"]. Το priority 350 είναι βολικό σημείο για έναν selector γιατί εκτελείται πριν από το built-in transport βήμα στο 750, αλλά δεν είναι από μόνο του μηχανισμός rotation.

Συνηθισμένα Λάθη στη Σειρά των Middleware
- Το middleware σας στο ίδιο priority με το
HttpProxyMiddleware(750): Αυτό δημιουργεί race condition όπου η dict-ordering του Scrapy — όχι η λογική σας — αποφασίζει ποιο middleware θα επεξεργαστεί πρώτο το request. Σύμπτωμα: διαλείπουσα, ανεξήγητη συμπεριφορά proxy. - Χρήση
setdefault()ήif "proxy" not in request.metaσε rotating selector: το αντιγραμμένο retry κρατάει το παλιό proxy. Σύμπτωμα: κάθε retry επαναλαμβάνει την ίδια αποτυχημένη διαδρομή. Διόρθωση: ανίχνευσε retry (π.χ.retry_times > 0) και αντικατέστησε ρητά την proxy τιμή που ελέγχει ο selector. - Απενεργοποίηση του
HttpProxyMiddlewareεντελώς: Κάποια tutorials λένε να βάλεις"scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware": Noneεπειδή «το custom middleware το χειρίζεται». Δεν το χειρίζεται — το custom middleware σου είναι selector, όχι transport layer. Αν απενεργοποιήσεις το built-in, τα proxy authentication headers δεν θα προστεθούν ποτέ και τα requests θα φύγουν σιωπηλά χωρίς αυθεντικοποίηση (ή δεν θα φύγουν καθόλου).
Βήμα 4: Σταματήστε να Κωδικοποιείτε Hardcoded τα Credentials του Proxy
Κάθε Scrapy proxy tutorial που βρήκα και βγαίνει πρώτο στις αναζητήσεις γράφει http://username:password@proxy.example.com:8080 κατευθείαν μέσα σε ένα αρχείο Python. Αυτό είναι ένα credential που μένει για πάντα στο Git history σου, εμφανίζεται σε κάθε clone, σε κάθε fork, σε κάθε log dump αν κάποιος είναι απρόσεκτος με print().
Στην πράξη υπάρχουν τρία επίπεδα για να το κάνεις σωστά:
| Μέθοδος | Ασφάλεια | Ευελιξία | Ιδανική Για |
|---|---|---|---|
| Hardcoded σε spider/settings.py | Κακή — τα secrets ζουν στο repo | Χαμηλή | Μόνο για γρήγορα τοπικά tests |
Περιβαλλοντική μεταβλητή http_proxy (native στο Scrapy) | Καλύτερη — έξω από τον κώδικα | Χαμηλή (ένα proxy) | CI/CD pipelines, Docker |
.env + python-dotenv + from_crawler | Η καλύτερη — έξω από τον κώδικα, ανά περιβάλλον | Υψηλή (πολλά proxies, rotation) | Production scrapers |
Η τρίτη επιλογή αξίζει να στηθεί σωστά. Εγκατέστησε το python-dotenv, δημιούργησε ένα .env αρχείο (και πρόσθεσέ το αμέσως στο .gitignore — το εννοώ, κάν’ το τώρα):
PROXY_USER=myuser
PROXY_PASSWORD=my$ecret!Pass
PROXY_HOST=proxy.example.com
PROXY_PORT=8080
Φόρτωσέ το στην κορυφή του 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")
Στη συνέχεια διάβασέ το με ασφάλεια μέσα στο middleware σου με from_crawler — αυτό είναι το μοτίβο που λύνει ακριβώς τη σύγχυση που βλέπω σε forums, όπου οι άνθρωποι ρωτούν «πώς το ορίζω πριν τρέξω scrapy crawl αν τα credentials αλλάζουν σε κάθε run;»:
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
Πρόσεξε την κλήση urllib.parse.quote() γύρω από τα credentials. Αν ο κωδικός σου περιέχει @, : ή / (και οι generators αγαπούν να βάζουν τέτοια), θα σπάσει το parsing του URL εκτός αν έχει γίνει πρώτα percent-encoding. Αυτή είναι μια διόρθωση μίας γραμμής που σε γλιτώνει από μια ντροπιαστική ώρα debugging για «invalid proxy URL» σφάλματα που δεν έχουν καμία σχέση με το αν το proxy είναι πραγματικά άκυρο.
Κράτα τα credentials έξω από logs και έλεγξε το percent-encoding με αντιπροσωπευτικές — αλλά ψεύτικες — τιμές. Το HTTPS tunneling, το SOCKS5 και τα credentials μη λατινικών χαρακτήρων έχουν όρια που εξαρτώνται από τον handler· μην υποθέσεις ότι ένα μοτίβο αυθεντικοποίησης δουλεύει παντού χωρίς ένα pinned integration test.

Βήμα 5: Προσθέστε Proxy Rotation
Ένα μόνο proxy — ακόμα κι αν είναι καλό — που κάνει 5.000 requests στο ίδιο site, τελικά θα επισημανθεί. Χρειάζεσαι pool.
Επιλογή Α — φτιάξ’ το μόνος σου. Είναι πραγματικά απλό:
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)
Αυτό δουλεύει για βασική χρήση, αλλά δεν έχει καμία επίγνωση για το ποια proxies είναι πραγματικά ζωντανά. Ρίχνεις ζάρια σε κάθε request.
Επιλογή Β — χρησιμοποίησε το scrapy-rotating-proxies. Αυτό το third-party πακέτο προσθέτει detection bans και αυτόματο backoff έτοιμα από το κουτί:
pip install scrapy-rotating-proxies
Θα πω κάτι με ειλικρίνεια εδώ: η τελευταία έκδοση στο PyPI είναι η 0.6.2, με ημερομηνία 2019, και το project είναι tagged ως Alpha. Δεν είναι απαραίτητα broken σε σύγχρονο Scrapy, αλλά το «δεν συντηρείται ενεργά από το 2019» δεν σημαίνει το ίδιο με «δοκιμασμένο σε production traffic του 2026». Κλείδωσε την έκδοση, δοκίμασέ το στα πραγματικά sites-στόχους σου και μην υποθέσεις ότι χειρίζεται authenticated proxy endpoints — σε μεγάλο βαθμό δεν το κάνει.
Βήμα 6: Φτιάξτε ένα Fault-Tolerant Proxy Middleware (Ανίχνευση Νεκρών Proxies)
Αυτή είναι η ενότητα που κάθε ανταγωνιστικό tutorial προσπερνάει τελείως, και είναι η διαφορά ανάμεσα σε ένα demo και σε κάτι που αντέχει ένα 6ωρο crawl χωρίς επίβλεψη.
| Σενάριο | Τι δείχνουν συνήθως τα tutorials | Τι προσθέτει αυτό το middleware |
|---|---|---|
| Το proxy επιστρέφει 407 | Δεν καλύπτεται | Το αντιμετωπίζει ως αποτυχία proxy authentication |
| Ο στόχος επιστρέφει 403/429 | Συχνά τα βάζουν όλα μαζί | Κρατά ξεχωριστά τα σήματα πολιτικής/rate από την υγεία του proxy |
| Το proxy κάνει timeout | Δεν καλύπτεται | Ρυθμιζόμενο threshold timeout, απομείωση health score |
| Όλα τα proxies είναι νεκρά | Δεν καλύπτεται | Graceful fallback ή παύση του crawl με καταγεγραμμένη προειδοποίηση |
| Το proxy «παίζει» (διαλείπουσα λειτουργία) | Δεν καλύπτεται | Περίοδος cool-down πριν ξαναμπεί στο pool |
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("Όλα τα proxies είναι ανθυγιεινά — παύση crawl")
raise IgnoreRequest("Δεν υπάρχουν διαθέσιμα υγιή proxies")
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
Μερικές σημειώσεις από πραγματική υλοποίηση: μην αντιμετωπίζεις κάθε 403 ως απόδειξη ότι το ίδιο το proxy είναι νεκρό — το RFC 9110 ορίζει το 403 ως «ο server κατάλαβε αλλά αρνείται», πράγμα που μπορεί εξίσου εύκολα να σημαίνει ότι τα headers ή η session σου φαίνονται ύποπτα, ανεξάρτητα από το proxy. Το 407, αντίθετα, σημαίνει ότι το proxy ειδικά απορρίπτει την αυθεντικοποίησή σου — αυτό είναι ισχυρότερο και πιο συγκεκριμένο σήμα. Το να τα βάζεις όλα αυτά στο ίδιο καλάθι είναι ο τρόπος να καίς υγιή proxies χωρίς λόγο.
Κράτα τα transient retry defaults του Scrapy ρητά σε production, ώστε οι reviewers να βλέπουν καθαρά την πολιτική:
RETRY_HTTP_CODES = [500, 502, 503, 504, 522, 524, 408, 429]
RETRY_TIMES = 2
Αυτά είναι τα defaults του Scrapy 2.17. Μην προσθέσεις μαζικά το 403 ή το 407: το 403 είναι απόρριψη από τον στόχο με πολλές πιθανές αιτίες, ενώ το 407 είναι πρόβλημα proxy-authentication που το γενικό retry αιτημάτων δεν θα διορθώσει. Αν ένας συγκεκριμένος συμβατικός όρος με τον στόχο δικαιολογεί retry για άλλο status, τεκμηρίωσε τον λόγο και δοκίμασέ τον ξεχωριστά.

Βήμα 7: Μια Μετρημένη Λογική Proxy-on-Retry
Κάποιοι εξουσιοδοτημένοι στόχοι μπορεί να επιστρέφουν έγκυρο περιεχόμενο απευθείας και να χρειάζονται proxy μόνο μετά από τεκμηριωμένη παροδική απόκριση. Αυτό μπορεί να μειώσει τα proxied bytes, αλλά δεν υπάρχει υπερασπίσιμη καθολική ποσοστιαία εξοικονόμηση ούτε portable ποσοστό επιτυχίας των direct requests. Μέτρησε το δικό σου workload πριν υιοθετήσεις το μοτίβο.
Χρησιμοποίησε το δημόσιο βοηθητικό get_retry_request() του Scrapy και κράτα την κλιμάκωση περιορισμένη σε status codes του συγκεκριμένου στόχου που έχεις ρητά ταξινομήσει. Αυτό το παράδειγμα αντιμετωπίζει τα 429 και 503 ως retryable σήματα πίεσης· αποκλείει σκόπιμα τα 403 και 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
Κατέγραψε direct valid-content rate, proxied valid-content rate, bytes ανά επιτυχημένο record, retries ανά επιτυχία και πρόσθετη καθυστέρηση. Ένα direct-first design είναι αποδεκτό μόνο όταν η άμεση πρόσβαση είναι εξουσιοδοτημένη και το σύστημα αποτυγχάνει κλειστά κάθε φορά που απαιτείται proxy. Η εξοικονόμηση είναι η μετρημένη μείωση στα proxied traffic — όχι ένα υποθετικό ποσοστό.
Όταν η DIY Διαχείριση Proxy Δεν Αξίζει
Θέλω να είμαι ξεκάθαρος αντί να προσποιούμαι ότι όλο το υπόλοιπο άρθρο ήταν άχρηστο: όλα όσα είδαμε είναι πραγματική, χρήσιμη μηχανική, και για πολλά projects — crawls μεγάλου όγκου, custom pipelines, οτιδήποτε απαιτεί πλήρη έλεγχο του request scheduling — είναι η σωστή επιλογή.
Αλλά αν ο πραγματικός σου στόχος είναι «πάρε δομημένα δεδομένα από αυτή τη σελίδα» και όχι «διαχειρίσου υποδομή proxy», τότε ένα extraction API μπορεί να είναι καλύτερο όριο. Η τρέχουσα τεκμηρίωση του POST /extract του Thunderbit δέχεται ένα page URL και ένα προαιρετικό JSON Schema· όταν το schema παραλείπεται, η υπηρεσία μπορεί να το δημιουργήσει από το περιεχόμενο της σελίδας. Το endpoint προσφέρει επίσης λειτουργίες render none, basic και full, μαζί με ελέγχους timeout και wait μετά το φόρτωμα. Η εξαγωγή μόνο με prompt δεν αποτελεί μέρος του τρέχοντος υποστηριζόμενου request surface, οπότε χτίσε production integrations γύρω από το documented schema contract. Αυτό μεταφέρει το extraction interface πίσω από ένα request· δεν δικαιολογεί όμως καμία καθολική υπόσχεση για κάθε anti-bot ή CAPTCHA στόχο.
| Παράγοντας | DIY Scrapy + Proxies | Εξαγωγή μέσω API (π.χ. Thunderbit) |
|---|---|---|
| Απαίτηση setup | Υψηλή — middleware, rotation, retry logic | Χαμηλή — ένα API call με schema |
| Συμπεριφορά δικτύου/rendering | Ρυθμίζεις handlers, proxies, headers και καθυστερήσεις | Ελεγχόμενη μέσω documented επιλογών API |
| Συντήρηση | Εσύ διαχειρίζεσαι selectors, υγεία pool και αλλαγές στόχων | Εσύ διαχειρίζεσαι ποιότητα schema, validation και συμπεριφορά integration |
| Μοντέλο κόστους | Χρεώσεις proxy + compute + χρόνος μηχανικού | Η τρέχουσα τεκμηρίωση αναφέρει 20 units ανά σελίδα (έλεγχος 2026-08-10) |
| Έλεγχος | Πλήρης — custom pipelines, αλυσίδα middleware | Περιορισμένος από τις δυνατότητες του API |
| Ιδανικό για | Πολύπλοκα crawls, υψηλό volume, custom λογική | Στοχευμένη εξαγωγή, prototyping, enrichment |
Αν κυρίως εξάγεις δομημένες σελίδες για λίστες leads, δεδομένα προϊόντων ή έρευνα, τρέξε ένα αντιπροσωπευτικό pilot με το Thunderbit Chrome Extension ή το API και σύγκρινε έγκυρα records, latency, units και χρόνο συντήρησης. Αν το project σου χρειάζεται custom crawl graphs και έλεγχο pipeline, το Scrapy παραμένει η ισχυρότερη επιλογή.
Μάθετε Περισσότερα
Συμβουλές & Συνηθισμένες Παγίδες
- Συμβουλή: Δοκίμασε proxies στο
httpbin.org/ipπριν τα στρέψεις σε πραγματικό στόχο. Είναι ο πιο γρήγορος τρόπος να επιβεβαιώσεις ότι η δρομολόγηση δουλεύει πριν προσθέσεις πάνω της πολυπλοκότητα. - Παγίδα: Να βάζεις proxy στα
request.headersαντί για τοrequest.meta. Αυτό είναι ένα πραγματικά συνηθισμένο bug που μοιάζει με typo — τοHttpProxyMiddlewareδιαβάζει μόνο τοmeta["proxy"], και μια προσπάθεια μέσω header θα αποτύχει σιωπηλά χωρίς προφανές σφάλμα. - Παγίδα: Να υποθέτεις ότι τα HTTP και HTTPS proxies ρυθμίζονται με τον ίδιο ακριβώς τρόπο. Ένα HTTP proxy URL που δρομολογεί σε HTTPS destination συνήθως λειτουργεί μέσω CONNECT tunneling υπό συμβατό handler, αλλά το να χρησιμοποιείς
https://scheme για το ίδιο το proxy endpoint είναι διαφορετική, λιγότερο υποστηριζόμενη ρύθμιση — μην τα μπερδέψεις. - Συμβουλή: Αν χρειάζεσαι υποστήριξη SOCKS5, έλεγξε πρώτα τις δυνατότητες του download handler της έκδοσης του Scrapy που έχεις. Ο πειραματικός Httpx handler του Scrapy 2.17 πρόσθεσε υποστήριξη SOCKS5 μέσω
httpx[socks], αλλά παραμένει χαρακτηρισμένος ως πειραματικός — μην στηρίξεις production εξάρτηση πάνω του χωρίς δική σου δοκιμή.
Εναλλακτικές Μέθοδοι
Πέρα από το scrapy-rotating-proxies, ορισμένες ομάδες δρομολογούν όλη τη λογική proxy μέσα από ένα provider gateway — ένα μοναδικό proxy URL όπου ο πάροχος αναλαμβάνει από πίσω τη rotation, τη session stickiness και το geo-targeting. Αυτό ανταλλάσσει λίγο έλεγχο με πολύ λιγότερο middleware code, και αξίζει να το κοστολογήσεις απέναντι σε ένα self-managed pool πριν το χτίσεις από το μηδέν.
Συμπέρασμα
Το να ορίσεις ένα proxy στο Scrapy θέλει μία γραμμή. Το να επιβιώσει αυτή η ρύθμιση σε πραγματικό production crawl απαιτεί ρητή πολιτική αποτυχίας, προστατευμένα credentials, ελεγμένα όρια handler και κώδικα selector που αντικαθιστά σκόπιμα τα αντιγραμμένα proxy metadata στο retry. Αν κρατήσεις δύο συνήθειες από εδώ, να είναι αυτές: fail closed όταν απαιτείται proxy, και ποτέ μην κάνεις commit έναν proxy password σε αρχείο Python.
Συχνές Ερωτήσεις
Πώς ρυθμίζω ένα προσαρμοσμένο proxy στο Scrapy με authentication;
Χρησιμοποίησε τη μορφή URL protocol://username:password@host:port, αλλά πρώτα κάνε percent-encode το username και το password με urllib.parse.quote() αν περιέχουν ειδικούς χαρακτήρες. Για production, διάβασε αυτά τα credentials μέσω μιας μεθόδου from_crawler που παίρνει τιμές από environment variables αντί να τα γράψεις hardcoded.
Ποιον αριθμό priority πρέπει να χρησιμοποιήσω για το custom proxy middleware μου στο Scrapy;
Το 350 είναι ένα συνηθισμένο selector priority επειδή τρέχει πριν από το HttpProxyMiddleware στο 750. Δεν εγγυάται rotation. Ένα retry παίρνει νέο proxy μόνο αν ο selector αναγνωρίσει το αντιγραμμένο retry request και αντικαταστήσει την προηγούμενη τιμή του meta["proxy"].
Πώς χειρίζομαι αυτόματα νεκρά proxies στο Scrapy;
Φτιάξε ένα middleware που παρακολουθεί μετρήσεις αποτυχίας ανά proxy στα process_response και process_exception, αφαιρεί τα proxies από το ενεργό pool όταν ξεπεραστεί ένα threshold αποτυχιών και τα ξαναβάζει μετά από περίοδο cool-down αντί να τα μπλοκάρει μόνιμα.
Μπορώ να χρησιμοποιήσω το Scrapy με SOCKS5 proxies;
Το πειραματικό HttpxDownloadHandler του Scrapy 2.17 τεκμηριώνει υποστήριξη SOCKS5 όταν είναι εγκατεστημένο το httpx[socks]. Ο default HTTP/1.1 handler δεν υποστηρίζει SOCKS proxies. Κλείδωσε handler/έκδοση και τρέξε integration test πριν θεωρήσεις αυτή τη διαδρομή έτοιμη για παραγωγή.
Πόσο μπορεί πραγματικά να εξοικονομήσει το μοτίβο proxy-on-retry σε κόστος proxy; Δεν υπάρχει φορητό ποσοστό. Μέτρησε το ποσοστό των εξουσιοδοτημένων requests που επιστρέφουν έγκυρο περιεχόμενο απευθείας, τα bytes που περνούν από κάθε tier proxy, τα retries ανά επιτυχημένο record και την πρόσθετη καθυστέρηση. Η παρατηρούμενη μείωση στα proxied bytes είναι η εξοικονόμησή σου· αν η direct-first πρόσβαση δεν είναι εξουσιοδοτημένη ή έγκυρη, μην χρησιμοποιείς αυτό το μοτίβο.


