-
Notifications
You must be signed in to change notification settings - Fork 2
systems proxy pool
Active contributors: Magnus Hedemark
Round-robin proxy pool for scrape-based engine adapters (DuckDuckGo, Google) with failure tracking and escalating cooloff. Designed to prevent CAPTCHA blocks and rate-limit bans when multiple requests originate from a single IP address. Supports both static proxy lists and dynamic single-endpoint proxies.
| Type | File | Description |
|---|---|---|
ProxyPool |
slopsearx/proxypool.py |
Round-robin proxy manager. Cycles through proxies, tracks consecutive failures, applies cooloff periods. |
get_proxy() |
slopsearx/proxypool.py |
Returns the next healthy proxy as an httpx-compatible dict ({"all://": "http://..."}), or None if no proxies are configured. |
report_failure() |
slopsearx/proxypool.py |
Marks a proxy as failed (CAPTCHA, 429, 403). Increments consecutive failure counter and sets cooloff timer. |
report_success() |
slopsearx/proxypool.py |
Resets the failure counter and clears cooloff for a successfully used proxy. |
from_config() |
slopsearx/proxypool.py |
Factory classmethod that creates a ProxyPool from an engine's config dict. Returns None if no proxy configuration is present. |
available_count |
slopsearx/proxypool.py |
Read-only property returning the number of proxies not currently on cooloff. |
total_count |
slopsearx/proxypool.py |
Read-only property returning the total number of proxies in the pool. |
ScrapeAdapter |
slopsearx/adapter.py |
Base class for scrape engines. Creates a ProxyPool via from_config() and delegates proxy selection/success/failure reporting. |
-
Initialization.
from_config()readsproxy_pool(list of proxy URLs) and/orscrape_proxy_url(dynamic single endpoint) from the engine's config dict. ReturnsNoneif neither is configured. -
Dynamic endpoint mode. If
scrape_proxy_urlis set, allget_proxy()calls return that single URL — no local rotation needed. This is used when an external proxy service (e.g., residential proxy provider) handles rotation. -
Static pool mode. If a list of proxies is provided,
get_proxy()cycles through them usingitertools.cycle()for round-robin distribution. -
Cooloff check. Before returning a proxy,
get_proxy()checks whether it is on cooloff (current time <cooloff_untiltimestamp). Proxies on cooloff are skipped. - Fail-open. If all proxies are on cooloff after a full rotation, the next proxy in the cycle is returned anyway (logging a warning). This prevents total query failure when the entire pool is temporarily exhausted.
When a request through a proxy fails (CAPTCHA, HTTP 429, HTTP 403):
-
Failure counter incremented.
report_failure()increments the consecutive failure count for that proxy. -
Cooloff duration calculated. Base cooloff is
proxy_cooloff_seconds(default 120). If failures >= 3, the duration is tripled. -
Cooloff timer set. The proxy becomes unavailable until
time.monotonic() + duration. -
Success resets.
report_success()clears both the failure counter and the cooloff timer.
Example cooloff progression:
- 1st failure: 120s cooloff
- 2nd failure: 120s cooloff
- 3rd failure: 360s cooloff (tripled)
- 4th+ failure: 360s cooloff (stays tripled)
get_proxy() returns httpx-compatible proxy dicts:
{"all://": "http://proxy1:8080"} # HTTP proxy for all protocolsThe _extract_url() static method handles extraction of the raw URL from any httpx proxy dict format by taking the first non-empty value.
ScrapeAdapter.__init__() (the base class for scrape engines like DDG and Google) creates a ProxyPool instance from the engine's config:
self._proxy_pool = ProxyPool.from_config(self.config)Three methods on ScrapeAdapter delegate to the pool:
def _get_proxy(self) -> dict[str, str] | None:
if self._proxy_pool is None:
return None
return self._proxy_pool.get_proxy()
def _report_proxy_success(self, proxy):
if self._proxy_pool is not None:
self._proxy_pool.report_success(proxy)
def _report_proxy_failure(self, proxy):
if self._proxy_pool is not None:
self._proxy_pool.report_failure(proxy)Scrape engines call _get_proxy() before making HTTP requests and pass the returned proxy dict to httpx. After the request, they call either _report_proxy_success() or _report_proxy_failure() based on the response.
Proxy configuration is per-engine in config.yaml:
engines:
duckduckgo:
enabled: true
proxy_pool:
- "http://proxy1:8080"
- "http://proxy2:8080"
- "http://proxy3:8080"
proxy_cooloff_seconds: 120
google:
enabled: true
scrape_proxy_url: "http://residential-proxy-service:9000"-
proxy_pool: list of static proxy URLs for round-robin rotation -
scrape_proxy_url: single dynamic proxy endpoint (external rotation) -
proxy_cooloff_seconds: base cooloff duration in seconds (default 120)
Only one of proxy_pool or scrape_proxy_url is typically used at a time. If both are present, scrape_proxy_url takes precedence and the static pool is bypassed.
-
ScrapeAdapter base class:
_get_proxy(),_report_proxy_success(), and_report_proxy_failure()methods onScrapeAdapterinslopsearx/adapter.pydelegate to the pool -
DuckDuckGo adapter: Uses pool via
ScrapeAdapter._get_proxy()before each scrape request, reports success/failure based on response status -
Google adapter: Uses pool via
ScrapeAdapter._get_proxy()before each scrape request, reports success/failure based on response status -
Config system: Proxy settings are defined in per-engine
EngineEntryconfig (fields:proxy_pool,scrape_proxy_url,proxy_cooloff_seconds)
- Adding new proxy selection strategy (e.g., least-recently-used, weighted random): modify
get_proxy()inproxypool.py - Changing cooloff escalation logic: modify
report_failure()— adjust thresholds, multipliers, or duration formula - Adding proxy health checks: add a periodic health probe method to
ProxyPool - Exposing proxy pool metrics: add Prometheus gauge metrics for
available_countandtotal_count - Adding authentication support: extend
ProxyPoolto handle proxy credentials
| File | Description |
|---|---|
slopsearx/proxypool.py |
ProxyPool class, round-robin rotation, cooloff tracking, from_config factory |
slopsearx/adapter.py |
ScrapeAdapter base class with proxy delegation methods (_get_proxy, _report_proxy_success/failure) |
slopsearx/config.py |
EngineEntry proxy config fields (proxy_pool, scrape_proxy_url, proxy_cooloff_seconds) |