Undetected Chrome browser driver with proxy support and captcha solving.
Project description
rtfox-browser
Undetected Chrome browser driver with SOCKS5 proxy, captcha solving and multiprocessing support.
Based on
This project is a fork of undetected-chromedriver by @UltrafunkAmsterdam.
We took the core Chrome driver stealth patching logic as a base and extended it with:
- SOCKS5 proxy support with authentication
- Built-in captcha solving module with plugin API
- Multiprocessing isolation (per-worker profiles and binaries)
- On-the-fly proxy replacement without browser restart
- Structured logging via loguru
- Improved cleanup and shutdown handling
Original project is licensed under GPL-3.0 — this fork inherits the same license.
Features
- 🛡️ Bypasses CloudFlare / hCaptcha / Imperva detection out of the box
- 🔒 SOCKS5 / HTTP / HTTPS proxy support with authentication
- 🔄 On-the-fly proxy replacement via
proxy_replacement()— no restart needed - 🧩 Built-in captcha solving module (2 solvers included)
- ⚙️ Custom captcha solver API — share your own solvers
- 🚀 Multiprocessing support
- 🖥️ Cross-platform: Linux, macOS, Windows
Installation
pip install rtfox-browser
Quick start
import rtfox_browser as uc
driver = uc.Chrome()
driver.get("https://example.com")
driver.quit()
Proxy
SOCKS5 with authentication
driver = uc.Chrome(
proxy={
"host": "1.2.3.4",
"port": 1080,
"type": "socks5",
"user": "username",
"pass": "password",
}
)
HTTP proxy (no auth)
driver = uc.Chrome(
proxy={
"host": "1.2.3.4",
"port": 8080,
"type": "http",
}
)
Named keyword arguments (alternative style)
from rtfox_browser.proxy_types import ProxyType
driver = uc.Chrome(
proxy_host="1.2.3.4",
proxy_port=1080,
proxy_type=ProxyType.SOCKS5,
proxy_user="username",
proxy_pass="password",
)
Proxy exceptions
| Exception | Cause |
|---|---|
ProxyError |
Base class for all proxy errors |
ProxyConnectionError |
Failed to connect to proxy (invalid host/port) |
ProxyAuthError |
Wrong login or password |
ProxyTimeoutError |
Proxy does not respond within timeout |
ProxyInvalidAddressError |
Malformed host or port format |
ProxyConfigError |
Missing or invalid proxy configuration fields |
from rtfox_browser.exceptions import (
ProxyError,
ProxyConnectionError,
ProxyAuthError,
ProxyTimeoutError,
ProxyInvalidAddressError,
ProxyConfigError,
)
try:
driver = uc.Chrome(proxy={
"host": "1.2.3.4",
"port": 1080,
"type": "socks5",
"user": "username",
"pass": "password",
})
except ProxyConfigError:
print("Invalid or incomplete proxy config")
except ProxyInvalidAddressError:
print("Malformed host or port")
except ProxyAuthError:
print("Wrong credentials")
except ProxyTimeoutError:
print("Proxy timed out")
except ProxyConnectionError:
print("Could not connect to proxy")
except ProxyError:
print("General proxy failure")
On-the-fly proxy replacement
Replace the active proxy at any time without restarting Chrome. A local transparent tunnel is always running, so switching works even on sessions started without a proxy.
driver = uc.Chrome()
# Switch to a new proxy
driver.proxy_replacement({
"host": "5.6.7.8",
"port": 1080,
"type": "socks5",
"user": "alice",
"pass": "secret",
})
# Switch to direct connection (no proxy)
driver.proxy_replacement(None)
# Named-argument style
driver.proxy_replacement(
host="5.6.7.8",
port=1080,
proxy_type="socks5",
user="alice",
password="secret",
)
By default proxy_replacement() clears browser cache and cookies after switching so Chrome opens fresh connections through the new proxy. Pass clear_connections=False to skip this step.
Multiprocessing
Each worker gets its own isolated Chrome profile and chromedriver binary.
worker_id is optional — if omitted, a random UUID is assigned automatically. Pass it explicitly only if you want predictable names in logs or file paths.
from multiprocessing import Pool
import rtfox_browser as uc
def run_worker(worker_id):
driver = uc.Chrome(worker_id=worker_id)
driver.get("https://example.com")
driver.quit()
with Pool(4) as pool:
pool.map(run_worker, ["w1", "w2", "w3", "w4"])
Without explicit worker_id:
def run_worker(_):
driver = uc.Chrome() # worker_id auto-assigned as UUID
driver.get("https://example.com")
driver.quit()
with Pool(4) as pool:
pool.map(run_worker, range(4))
Captcha solving
rtfox-browser includes a captcha module with 2 built-in solvers.
Solvers are loaded automatically from a solvers/ directory in your working directory.
Setup
from rtfox_browser.captcha import CaptchaService
driver = uc.Chrome()
captcha = CaptchaService(api_key="YOUR_2CAPTCHA_KEY", driver=driver)
# Check available solvers
print(captcha.available())
# ['ebay_hcaptcha', 'aws_image']
Solve a captcha
driver.get("https://example.com")
captcha.ebay_hcaptcha()
Custom solvers directory
captcha = CaptchaService(
api_key="YOUR_KEY",
driver=driver,
solvers_dir="/path/to/your/solvers",
)
Creating your own captcha solver
Drop a file into the solvers/ folder — it will be picked up automatically. No registration needed.
1. Create solvers/my_solver.py:
from rtfox_browser.captcha import BaseCaptchaSolver
class MySolver(BaseCaptchaSolver):
name = "my_captcha" # becomes the method name on CaptchaService
def solve(self, **kwargs) -> bool:
driver = self.driver # Selenium WebDriver
api_key = self.api_key # 2captcha API key
# your solving logic here
...
return True # True = solved, False = failed
2. Use it:
captcha = CaptchaService(api_key="KEY", driver=driver)
captcha.my_captcha()
3. Or register manually at runtime:
captcha.register(MySolver)
captcha.my_captcha()
BaseCaptchaSolver API
| Property | Type | Description |
|---|---|---|
name |
str |
Method name exposed on CaptchaService |
self.driver |
WebDriver |
The active browser instance |
self.api_key |
str |
API key passed to CaptchaService |
solve(**kwargs) |
bool |
Your solving logic, return True on success |
ProxyConfig reference
You can construct proxy config directly using ProxyConfig:
from rtfox_browser.proxy_types import ProxyConfig, ProxyType
# From a dict
config = ProxyConfig.from_dict({
"host": "1.2.3.4",
"port": 8080,
"type": "socks5",
"user": "alice",
"pass": "secret",
})
# From named arguments
config = ProxyConfig.from_args(
host="1.2.3.4",
port=8080,
proxy_type=ProxyType.SOCKS5,
user="alice",
password="secret",
)
# Pass directly to Chrome
driver = uc.Chrome(proxy=config)
# Or use for proxy_replacement
driver.proxy_replacement(config)
Supported proxy types: socks5, http, https.
Cross-platform support
| OS | Supported |
|---|---|
| Linux | ✅ |
| macOS | ✅ |
| Windows | ✅ |
Community
💬 Telegram: @rtf_labs_studio 🎥 YouTube: RTF Labs Studio
License
GPL-3.0 © rtf-labs-studio
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rtfox_browser-0.0.2.tar.gz.
File metadata
- Download URL: rtfox_browser-0.0.2.tar.gz
- Upload date:
- Size: 36.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17724e76c9e389774949994dc06c68eb20f5497c2677f74757f71a92d27cc9e4
|
|
| MD5 |
abf984374c719ca58b293298bf4c9bfc
|
|
| BLAKE2b-256 |
9ac03bde1507cf604a6de234b3ead21e9f85254c0fbf1e1ff4174dc16abaad72
|
File details
Details for the file rtfox_browser-0.0.2-py3-none-any.whl.
File metadata
- Download URL: rtfox_browser-0.0.2-py3-none-any.whl
- Upload date:
- Size: 37.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60b0f8dc981dae6665440b00e2636d5620dbb3c12942459008aa3b10881e1aa3
|
|
| MD5 |
893627d6346875e62a4bb376c41b2fc5
|
|
| BLAKE2b-256 |
5f47967e81278bf3e867a0e8937e5a44039be2e244e545f5fe78a43528550e8f
|