mirror of
https://github.com/CloakHQ/CloakBrowser.git
synced 2026-06-23 11:41:46 +02:00
fix: replace page.wait_for_timeout with time.sleep to avoid CDP leak
page.wait_for_timeout() sends CDP protocol commands that reCAPTCHA and other antibot systems detect. Replaced with time.sleep() (Python) which is invisible to the browser. - examples/stealth_test.py: 7 replacements - examples/recaptcha_score.py: 1 replacement - tests/test_stealth.py: 7 replacements - README.md + js/README.md: added reCAPTCHA troubleshooting section - Bump version to 0.2.2
This commit is contained in:
@@ -535,6 +535,39 @@ You do NOT need `playwright install chromium`. CloakBrowser downloads its own bi
|
||||
playwright install-deps chromium
|
||||
```
|
||||
|
||||
**reCAPTCHA v3 scores are low (0.1–0.3)**
|
||||
|
||||
Avoid `page.wait_for_timeout()` — it sends CDP protocol commands that reCAPTCHA detects. Use native sleep instead:
|
||||
|
||||
```python
|
||||
# Bad — sends CDP commands, reCAPTCHA detects this
|
||||
page.wait_for_timeout(3000)
|
||||
|
||||
# Good — invisible to the browser
|
||||
import time
|
||||
time.sleep(3)
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Bad — sends CDP commands
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Good — invisible to the browser
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
```
|
||||
|
||||
Other tips for maximizing reCAPTCHA scores:
|
||||
- **Use Playwright, not Puppeteer** — Puppeteer sends more CDP protocol traffic that reCAPTCHA detects ([details](#puppeteer))
|
||||
- **Use residential proxies** — datacenter IPs are flagged by IP reputation, not browser fingerprint
|
||||
- **Spend 15+ seconds on the page** before triggering reCAPTCHA — short visits score lower
|
||||
- **Space out requests** — back-to-back `grecaptcha.execute()` calls from the same session get penalized. Wait 30+ seconds between pages with reCAPTCHA
|
||||
- **Use a fixed fingerprint seed** (`--fingerprint=12345`) for consistent device identity across sessions
|
||||
- **Use `page.type()` instead of `page.fill()`** for form filling — `fill()` sets values directly without keyboard events, which reCAPTCHA's behavioral analysis flags. `type()` with a delay simulates real keystrokes:
|
||||
```python
|
||||
page.type("#email", "user@example.com", delay=50)
|
||||
```
|
||||
- **Minimize `page.evaluate()` calls** before the reCAPTCHA check fires — each one sends CDP traffic
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Is this legal?**
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.2.1"
|
||||
__version__ = "0.2.2"
|
||||
|
||||
@@ -5,6 +5,8 @@ Expected: 0.9 (human-level) with cloakbrowser.
|
||||
Default Playwright typically scores 0.1-0.3.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from cloakbrowser import launch
|
||||
|
||||
browser = launch(headless=True)
|
||||
@@ -18,7 +20,7 @@ page.wait_for_load_state("networkidle")
|
||||
button = page.query_selector("button")
|
||||
if button:
|
||||
button.click()
|
||||
page.wait_for_timeout(3000)
|
||||
time.sleep(3)
|
||||
|
||||
# Extract score from page
|
||||
content = page.content()
|
||||
|
||||
@@ -27,7 +27,7 @@ for i, arg in enumerate(sys.argv):
|
||||
def test_bot_sannysoft(page):
|
||||
"""bot.sannysoft.com — classic bot detection checks."""
|
||||
page.goto("https://bot.sannysoft.com", wait_until="networkidle", timeout=30000)
|
||||
page.wait_for_timeout(3000)
|
||||
time.sleep(3)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const rows = document.querySelectorAll('table tr');
|
||||
@@ -53,7 +53,7 @@ def test_bot_sannysoft(page):
|
||||
def test_bot_incolumitas(page):
|
||||
"""bot.incolumitas.com — comprehensive 30+ check bot detection."""
|
||||
page.goto("https://bot.incolumitas.com", wait_until="networkidle", timeout=30000)
|
||||
page.wait_for_timeout(12000) # needs time to run all detection tests
|
||||
time.sleep(12) # needs time to run all detection tests
|
||||
|
||||
# Site outputs JSON blocks in page text, not HTML tables
|
||||
results = page.evaluate("""() => {
|
||||
@@ -74,7 +74,7 @@ def test_bot_incolumitas(page):
|
||||
def test_browserscan(page):
|
||||
"""browserscan.net/bot-detection — WebDriver, UA, CDP, Navigator checks."""
|
||||
page.goto("https://www.browserscan.net/bot-detection", wait_until="networkidle", timeout=30000)
|
||||
page.wait_for_timeout(5000)
|
||||
time.sleep(5)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const items = document.querySelectorAll('[class*="result"], [class*="item"], [class*="check"]');
|
||||
@@ -95,7 +95,7 @@ def test_browserscan(page):
|
||||
def test_deviceandbrowserinfo(page):
|
||||
"""deviceandbrowserinfo.com/are_you_a_bot — fingerprint + behavioral detection."""
|
||||
page.goto("https://deviceandbrowserinfo.com/are_you_a_bot", wait_until="domcontentloaded", timeout=30000)
|
||||
page.wait_for_timeout(8000)
|
||||
time.sleep(8)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const text = document.body.innerText;
|
||||
@@ -120,12 +120,12 @@ def test_deviceandbrowserinfo(page):
|
||||
def test_fingerprintjs(page):
|
||||
"""demo.fingerprint.com/web-scraping — industry-standard bot detection."""
|
||||
page.goto("https://demo.fingerprint.com/web-scraping", wait_until="domcontentloaded", timeout=30000)
|
||||
page.wait_for_timeout(8000)
|
||||
time.sleep(8)
|
||||
|
||||
# Click search to trigger bot detection — bots get blocked, humans see flights
|
||||
try:
|
||||
page.click("button:has-text('Search')", timeout=5000)
|
||||
page.wait_for_timeout(5000)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -147,7 +147,7 @@ def test_recaptcha(page):
|
||||
timeout=30000,
|
||||
)
|
||||
# Page auto-submits via grecaptcha.execute() — wait for backend response
|
||||
page.wait_for_timeout(8000)
|
||||
time.sleep(8)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const text = document.body.innerText;
|
||||
|
||||
@@ -184,6 +184,28 @@ const page = await browser.newPage();
|
||||
- Node.js >= 18
|
||||
- One of: `playwright-core` >= 1.40 or `puppeteer-core` >= 21
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**reCAPTCHA v3 scores are low (0.1–0.3)**
|
||||
|
||||
Avoid `page.waitForTimeout()` — it sends CDP protocol commands that reCAPTCHA detects. Use native sleep instead:
|
||||
|
||||
```javascript
|
||||
// Bad — sends CDP commands, reCAPTCHA detects this
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
// Good — invisible to the browser
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
```
|
||||
|
||||
Other tips for maximizing reCAPTCHA scores:
|
||||
- **Use Playwright, not Puppeteer** — Puppeteer sends more CDP protocol traffic that reCAPTCHA detects ([details](#puppeteer))
|
||||
- **Use residential proxies** — datacenter IPs are flagged by IP reputation, not browser fingerprint
|
||||
- **Spend 15+ seconds on the page** before triggering reCAPTCHA — short visits score lower
|
||||
- **Space out requests** — back-to-back `grecaptcha.execute()` calls from the same session get penalized. Wait 30+ seconds between pages with reCAPTCHA
|
||||
- **Use a fixed fingerprint seed** (`--fingerprint=12345`) for consistent device identity across sessions
|
||||
- **Minimize `page.evaluate()` calls** before the reCAPTCHA check fires — each one sends CDP traffic
|
||||
|
||||
## Links
|
||||
|
||||
- 🌐 [Website](https://cloakbrowser.dev)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cloakbrowser",
|
||||
"version": "0.2.1",
|
||||
"version": "0.2.2",
|
||||
"description": "Stealth Chromium that passes every bot detection test. Drop-in Playwright/Puppeteer replacement with source-level fingerprint patches.",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -5,6 +5,7 @@ bot detection checks. They require network access.
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
import pytest
|
||||
from cloakbrowser import launch
|
||||
@@ -89,7 +90,7 @@ class TestBotDetectionSites:
|
||||
def test_bot_sannysoft(self, page):
|
||||
"""bot.sannysoft.com — all checks should pass (0 failures)."""
|
||||
page.goto("https://bot.sannysoft.com", wait_until="networkidle", timeout=30000)
|
||||
page.wait_for_timeout(3000)
|
||||
time.sleep(3)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const rows = document.querySelectorAll('table tr');
|
||||
@@ -115,7 +116,7 @@ class TestBotDetectionSites:
|
||||
def test_bot_incolumitas(self, page):
|
||||
"""bot.incolumitas.com — max 1 failure (WEBDRIVER false positive expected)."""
|
||||
page.goto("https://bot.incolumitas.com", wait_until="networkidle", timeout=30000)
|
||||
page.wait_for_timeout(12000)
|
||||
time.sleep(12)
|
||||
|
||||
# Known acceptable failures (not browser fingerprint issues):
|
||||
# - WEBDRIVER: spec-level false positive across all builds
|
||||
@@ -138,7 +139,7 @@ class TestBotDetectionSites:
|
||||
def test_browserscan(self, page):
|
||||
"""BrowserScan bot detection — 0 abnormal checks."""
|
||||
page.goto("https://www.browserscan.net/bot-detection", wait_until="networkidle", timeout=30000)
|
||||
page.wait_for_timeout(5000)
|
||||
time.sleep(5)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const text = document.body.innerText;
|
||||
@@ -157,7 +158,7 @@ class TestBotDetectionSites:
|
||||
def test_device_and_browser_info(self, page):
|
||||
"""deviceandbrowserinfo.com — isBot must be false."""
|
||||
page.goto("https://deviceandbrowserinfo.com/are_you_a_bot", wait_until="domcontentloaded", timeout=30000)
|
||||
page.wait_for_timeout(8000)
|
||||
time.sleep(8)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const text = document.body.innerText;
|
||||
@@ -179,11 +180,11 @@ class TestBotDetectionSites:
|
||||
def test_fingerprintjs(self, page):
|
||||
"""FingerprintJS — must not be blocked, should see flight data."""
|
||||
page.goto("https://demo.fingerprint.com/web-scraping", wait_until="domcontentloaded", timeout=30000)
|
||||
page.wait_for_timeout(8000)
|
||||
time.sleep(8)
|
||||
|
||||
try:
|
||||
page.click("button:has-text('Search')", timeout=5000)
|
||||
page.wait_for_timeout(5000)
|
||||
time.sleep(5)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -205,7 +206,7 @@ class TestBotDetectionSites:
|
||||
wait_until="domcontentloaded",
|
||||
timeout=60000,
|
||||
)
|
||||
page.wait_for_timeout(8000)
|
||||
time.sleep(8)
|
||||
|
||||
results = page.evaluate("""() => {
|
||||
const text = document.body.innerText;
|
||||
|
||||
Reference in New Issue
Block a user