unrealon 2.0.10__py3-none-any.whl → 2.0.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {unrealon-2.0.10.dist-info → unrealon-2.0.11.dist-info}/METADATA +1 -1
- {unrealon-2.0.10.dist-info → unrealon-2.0.11.dist-info}/RECORD +14 -14
- unrealon_browser/managers/logger_bridge.py +3 -3
- unrealon_browser/managers/script_manager.py +45 -26
- unrealon_browser/stealth/{bypass_techniques.pyc → bypass_techniques.py} +0 -0
- unrealon_browser/stealth/manager.py +507 -0
- unrealon_browser/stealth/nodriver_stealth.py +432 -0
- unrealon_browser/stealth/playwright_stealth.py +179 -0
- unrealon_browser/stealth/scanner_tester.py +355 -0
- unrealon_browser/stealth/undetected_chrome.py +361 -0
- unrealon_browser/stealth/manager.pyc +0 -0
- unrealon_browser/stealth/nodriver_stealth.pyc +0 -0
- unrealon_browser/stealth/playwright_stealth.pyc +0 -0
- unrealon_browser/stealth/scanner_tester.pyc +0 -0
- unrealon_browser/stealth/undetected_chrome.pyc +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.11.dist-info}/LICENSE +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.11.dist-info}/WHEEL +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.11.dist-info}/entry_points.txt +0 -0
- {unrealon-2.0.10.dist-info → unrealon-2.0.11.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NoDriverStealth - Integration with NoDriver
|
|
3
|
+
|
|
4
|
+
Provides integration with NoDriver for next-generation bot detection bypass:
|
|
5
|
+
- Async-first architecture
|
|
6
|
+
- No external ChromeDriver dependency
|
|
7
|
+
- Built-in stealth capabilities
|
|
8
|
+
- Advanced BotD bypass
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import logging
|
|
12
|
+
import asyncio
|
|
13
|
+
from typing import Dict, Any, Optional, List
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
logger.setLevel(logging.DEBUG)
|
|
17
|
+
|
|
18
|
+
class NoDriverStealth:
|
|
19
|
+
"""
|
|
20
|
+
Integration with NoDriver
|
|
21
|
+
|
|
22
|
+
NoDriver is the next-generation successor to undetected-chromedriver
|
|
23
|
+
with async-first architecture and built-in stealth capabilities
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, logger_bridge=None):
|
|
27
|
+
"""Initialize NoDriverStealth"""
|
|
28
|
+
self.logger_bridge = logger_bridge
|
|
29
|
+
|
|
30
|
+
def _logger(self, message: str, level: str = "info") -> None:
|
|
31
|
+
"""Private wrapper for logger with bridge support"""
|
|
32
|
+
if self.logger_bridge:
|
|
33
|
+
if level == "info":
|
|
34
|
+
self.logger_bridge.log_info(message)
|
|
35
|
+
elif level == "error":
|
|
36
|
+
self.logger_bridge.log_error(message)
|
|
37
|
+
elif level == "warning":
|
|
38
|
+
self.logger_bridge.log_warning(message)
|
|
39
|
+
else:
|
|
40
|
+
self.logger_bridge.log_info(message)
|
|
41
|
+
else:
|
|
42
|
+
if level == "info":
|
|
43
|
+
logger.info(message)
|
|
44
|
+
elif level == "error":
|
|
45
|
+
logger.error(message)
|
|
46
|
+
elif level == "warning":
|
|
47
|
+
logger.warning(message)
|
|
48
|
+
else:
|
|
49
|
+
logger.info(message)
|
|
50
|
+
|
|
51
|
+
def get_nodriver_config(self) -> Dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Get optimized configuration for NoDriver
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Dict with NoDriver configuration
|
|
57
|
+
"""
|
|
58
|
+
return {
|
|
59
|
+
"headless": False, # Recommend headed for best stealth
|
|
60
|
+
"browser_args": self.get_nodriver_arguments(),
|
|
61
|
+
"user_data_dir": None, # Use temporary profile
|
|
62
|
+
"lang": "en-US",
|
|
63
|
+
"sandbox": False,
|
|
64
|
+
"incognito": False, # Regular mode for better stealth
|
|
65
|
+
"host": "localhost",
|
|
66
|
+
"port": 0, # Auto-assign port
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
def get_nodriver_arguments(self) -> List[str]:
|
|
70
|
+
"""
|
|
71
|
+
Get optimized browser arguments for NoDriver
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
List of browser arguments for maximum stealth
|
|
75
|
+
"""
|
|
76
|
+
return [
|
|
77
|
+
# Core stealth arguments
|
|
78
|
+
"--no-first-run",
|
|
79
|
+
"--no-default-browser-check",
|
|
80
|
+
"--disable-blink-features=AutomationControlled",
|
|
81
|
+
"--disable-features=VizDisplayCompositor",
|
|
82
|
+
|
|
83
|
+
# Enhanced stealth for NoDriver
|
|
84
|
+
"--disable-web-security",
|
|
85
|
+
"--disable-client-side-phishing-detection",
|
|
86
|
+
"--disable-component-extensions-with-background-pages",
|
|
87
|
+
"--disable-default-apps",
|
|
88
|
+
"--disable-domain-reliability",
|
|
89
|
+
"--disable-background-networking",
|
|
90
|
+
"--disable-translate",
|
|
91
|
+
"--disable-ipc-flooding-protection",
|
|
92
|
+
|
|
93
|
+
# Performance optimization
|
|
94
|
+
"--no-sandbox",
|
|
95
|
+
"--disable-dev-shm-usage",
|
|
96
|
+
"--disable-background-timer-throttling",
|
|
97
|
+
"--disable-backgrounding-occluded-windows",
|
|
98
|
+
"--disable-renderer-backgrounding",
|
|
99
|
+
"--disable-field-trial-config",
|
|
100
|
+
"--disable-back-forward-cache",
|
|
101
|
+
|
|
102
|
+
# System integration
|
|
103
|
+
"--disable-hang-monitor",
|
|
104
|
+
"--disable-prompt-on-repost",
|
|
105
|
+
"--disable-sync",
|
|
106
|
+
"--metrics-recording-only",
|
|
107
|
+
"--no-report-upload",
|
|
108
|
+
|
|
109
|
+
# Window and display
|
|
110
|
+
"--window-size=1366,768",
|
|
111
|
+
"--start-maximized",
|
|
112
|
+
|
|
113
|
+
# Media settings
|
|
114
|
+
"--mute-audio",
|
|
115
|
+
"--disable-audio-output",
|
|
116
|
+
|
|
117
|
+
# Extensions (minimal for realism)
|
|
118
|
+
"--disable-extensions-except=",
|
|
119
|
+
|
|
120
|
+
# Memory optimization
|
|
121
|
+
"--max_old_space_size=4096",
|
|
122
|
+
"--memory-pressure-off",
|
|
123
|
+
|
|
124
|
+
# Additional NoDriver optimizations
|
|
125
|
+
"--disable-logging",
|
|
126
|
+
"--disable-gpu-logging",
|
|
127
|
+
"--silent",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
async def create_nodriver_browser(self, **kwargs) -> Optional[Any]:
|
|
131
|
+
"""
|
|
132
|
+
Create NoDriver browser instance
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
**kwargs: Additional configuration options
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
NoDriver browser instance or None if failed
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
import nodriver as uc
|
|
142
|
+
|
|
143
|
+
self._logger("🚀 Creating NoDriver browser...", "info")
|
|
144
|
+
|
|
145
|
+
# Get base configuration
|
|
146
|
+
config = self.get_nodriver_config()
|
|
147
|
+
config.update(kwargs)
|
|
148
|
+
|
|
149
|
+
# Create browser
|
|
150
|
+
browser = await uc.start(**config)
|
|
151
|
+
|
|
152
|
+
# Apply additional stealth measures
|
|
153
|
+
await self.apply_nodriver_stealth(browser)
|
|
154
|
+
|
|
155
|
+
self._logger("✅ NoDriver browser created successfully", "info")
|
|
156
|
+
return browser
|
|
157
|
+
|
|
158
|
+
except ImportError as e:
|
|
159
|
+
self._logger(f"❌ NoDriver not available: {e}", "error")
|
|
160
|
+
return None
|
|
161
|
+
except Exception as e:
|
|
162
|
+
self._logger(f"❌ Failed to create NoDriver browser: {e}", "error")
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
async def apply_nodriver_stealth(self, browser) -> bool:
|
|
166
|
+
"""
|
|
167
|
+
Apply additional stealth measures to NoDriver browser
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
browser: NoDriver browser instance
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
True if successful
|
|
174
|
+
"""
|
|
175
|
+
try:
|
|
176
|
+
self._logger("🛡️ Applying NoDriver stealth measures...", "info")
|
|
177
|
+
|
|
178
|
+
# Get the main tab/page
|
|
179
|
+
page = await browser.get("about:blank")
|
|
180
|
+
|
|
181
|
+
# Apply comprehensive stealth scripts
|
|
182
|
+
await self.apply_stealth_scripts(page)
|
|
183
|
+
|
|
184
|
+
self._logger("✅ NoDriver stealth measures applied", "info")
|
|
185
|
+
return True
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
self._logger(f"❌ Failed to apply NoDriver stealth: {e}", "error")
|
|
189
|
+
return False
|
|
190
|
+
|
|
191
|
+
async def apply_stealth_scripts(self, page) -> None:
|
|
192
|
+
"""
|
|
193
|
+
Apply comprehensive stealth scripts to NoDriver page
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
page: NoDriver page instance
|
|
197
|
+
"""
|
|
198
|
+
# Enhanced webdriver removal
|
|
199
|
+
webdriver_script = """
|
|
200
|
+
// Comprehensive webdriver removal for NoDriver
|
|
201
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
202
|
+
get: () => undefined,
|
|
203
|
+
configurable: true
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Remove from prototype
|
|
207
|
+
if (navigator.webdriver !== undefined) {
|
|
208
|
+
delete Object.getPrototypeOf(navigator).webdriver;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Remove automation indicators
|
|
212
|
+
['__webdriver_evaluate', '__selenium_evaluate', '__webdriver_script_function',
|
|
213
|
+
'__fxdriver_evaluate', '__driver_unwrapped', '__webdriver_unwrapped',
|
|
214
|
+
'_Selenium_IDE_Recorder', '_selenium', 'calledSelenium',
|
|
215
|
+
'_WEBDRIVER_ELEM_CACHE', 'ChromeDriverw', '$cdc_asdjflasutopfhvcZLmcfl_'].forEach(prop => {
|
|
216
|
+
delete window[prop];
|
|
217
|
+
delete document[prop];
|
|
218
|
+
});
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
# Chrome object creation
|
|
222
|
+
chrome_script = """
|
|
223
|
+
// Create realistic Chrome object for NoDriver
|
|
224
|
+
if (!window.chrome) {
|
|
225
|
+
window.chrome = {
|
|
226
|
+
runtime: {
|
|
227
|
+
onConnect: null,
|
|
228
|
+
onMessage: null,
|
|
229
|
+
sendMessage: () => {},
|
|
230
|
+
connect: () => ({
|
|
231
|
+
onMessage: { addListener: () => {}, removeListener: () => {} },
|
|
232
|
+
onDisconnect: { addListener: () => {}, removeListener: () => {} },
|
|
233
|
+
postMessage: () => {}
|
|
234
|
+
}),
|
|
235
|
+
id: undefined
|
|
236
|
+
},
|
|
237
|
+
app: {
|
|
238
|
+
isInstalled: false
|
|
239
|
+
},
|
|
240
|
+
csi: () => {},
|
|
241
|
+
loadTimes: () => ({
|
|
242
|
+
requestTime: Date.now() / 1000,
|
|
243
|
+
startLoadTime: Date.now() / 1000,
|
|
244
|
+
commitLoadTime: Date.now() / 1000,
|
|
245
|
+
finishDocumentLoadTime: Date.now() / 1000,
|
|
246
|
+
finishLoadTime: Date.now() / 1000,
|
|
247
|
+
firstPaintTime: Date.now() / 1000,
|
|
248
|
+
firstPaintAfterLoadTime: 0,
|
|
249
|
+
navigationType: 'Other'
|
|
250
|
+
})
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
# Plugin spoofing
|
|
256
|
+
plugin_script = """
|
|
257
|
+
// Plugin spoofing for NoDriver
|
|
258
|
+
const plugins = [
|
|
259
|
+
{
|
|
260
|
+
name: 'Chrome PDF Plugin',
|
|
261
|
+
filename: 'internal-pdf-viewer',
|
|
262
|
+
description: 'Portable Document Format',
|
|
263
|
+
length: 1
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'Chrome PDF Viewer',
|
|
267
|
+
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
|
|
268
|
+
description: 'Portable Document Format',
|
|
269
|
+
length: 1
|
|
270
|
+
}
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
Object.defineProperty(navigator, 'plugins', {
|
|
274
|
+
get: () => plugins,
|
|
275
|
+
configurable: true
|
|
276
|
+
});
|
|
277
|
+
"""
|
|
278
|
+
|
|
279
|
+
# WebGL spoofing
|
|
280
|
+
webgl_script = """
|
|
281
|
+
// WebGL spoofing for NoDriver
|
|
282
|
+
const originalGetParameter = WebGLRenderingContext.prototype.getParameter;
|
|
283
|
+
WebGLRenderingContext.prototype.getParameter = function(parameter) {
|
|
284
|
+
if (parameter === 37445) return 'Intel Inc.';
|
|
285
|
+
if (parameter === 37446) return 'Intel Iris OpenGL Engine';
|
|
286
|
+
return originalGetParameter.call(this, parameter);
|
|
287
|
+
};
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
# Apply all scripts
|
|
291
|
+
scripts = [webdriver_script, chrome_script, plugin_script, webgl_script]
|
|
292
|
+
|
|
293
|
+
for script in scripts:
|
|
294
|
+
try:
|
|
295
|
+
await page.evaluate(script)
|
|
296
|
+
except Exception as e:
|
|
297
|
+
self._logger(f"⚠️ Script application warning: {e}", "warning")
|
|
298
|
+
|
|
299
|
+
async def test_nodriver_stealth(self, test_url: str) -> Dict[str, Any]:
|
|
300
|
+
"""
|
|
301
|
+
Test NoDriver stealth effectiveness
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
test_url: URL to test stealth effectiveness
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Dict with test results
|
|
308
|
+
"""
|
|
309
|
+
browser = None
|
|
310
|
+
try:
|
|
311
|
+
self._logger(f"🧪 Testing NoDriver stealth on {test_url}...", "info")
|
|
312
|
+
|
|
313
|
+
# Create browser
|
|
314
|
+
browser = await self.create_nodriver_browser(headless=True)
|
|
315
|
+
if not browser:
|
|
316
|
+
return {
|
|
317
|
+
"success": False,
|
|
318
|
+
"error": "Failed to create NoDriver browser"
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# Navigate to test page
|
|
322
|
+
page = await browser.get(test_url)
|
|
323
|
+
|
|
324
|
+
# Wait for page to load
|
|
325
|
+
await asyncio.sleep(5)
|
|
326
|
+
|
|
327
|
+
# Extract detection results
|
|
328
|
+
try:
|
|
329
|
+
results = await page.evaluate("""
|
|
330
|
+
() => {
|
|
331
|
+
return {
|
|
332
|
+
userAgent: navigator.userAgent,
|
|
333
|
+
webdriver: navigator.webdriver !== undefined,
|
|
334
|
+
chrome: !!window.chrome,
|
|
335
|
+
plugins: navigator.plugins.length,
|
|
336
|
+
languages: navigator.languages ? navigator.languages.length : 0,
|
|
337
|
+
botDetectionResults: window.botDetectionResults || null
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
""")
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
"success": True,
|
|
344
|
+
"results": results,
|
|
345
|
+
"is_bot": results.get("webdriver", False),
|
|
346
|
+
"user_agent": results.get("userAgent", ""),
|
|
347
|
+
"chrome_object": results.get("chrome", False),
|
|
348
|
+
"plugins_count": results.get("plugins", 0),
|
|
349
|
+
"bot_detection": results.get("botDetectionResults")
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
except Exception as e:
|
|
353
|
+
return {
|
|
354
|
+
"success": False,
|
|
355
|
+
"error": f"Failed to extract results: {e}"
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
except Exception as e:
|
|
359
|
+
self._logger(f"❌ NoDriver test failed: {e}", "error")
|
|
360
|
+
return {
|
|
361
|
+
"success": False,
|
|
362
|
+
"error": str(e)
|
|
363
|
+
}
|
|
364
|
+
finally:
|
|
365
|
+
if browser:
|
|
366
|
+
try:
|
|
367
|
+
browser.stop()
|
|
368
|
+
except:
|
|
369
|
+
pass
|
|
370
|
+
|
|
371
|
+
async def create_stealth_page(self, browser, url: str):
|
|
372
|
+
"""
|
|
373
|
+
Create a new page with full stealth applied
|
|
374
|
+
|
|
375
|
+
Args:
|
|
376
|
+
browser: NoDriver browser instance
|
|
377
|
+
url: URL to navigate to
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
NoDriver page with stealth applied
|
|
381
|
+
"""
|
|
382
|
+
try:
|
|
383
|
+
# Create new page
|
|
384
|
+
page = await browser.get(url)
|
|
385
|
+
|
|
386
|
+
# Apply stealth scripts
|
|
387
|
+
await self.apply_stealth_scripts(page)
|
|
388
|
+
|
|
389
|
+
return page
|
|
390
|
+
|
|
391
|
+
except Exception as e:
|
|
392
|
+
self._logger(f"❌ Failed to create stealth page: {e}", "error")
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
def get_stealth_info(self) -> Dict[str, Any]:
|
|
396
|
+
"""
|
|
397
|
+
Get information about NoDriver capabilities
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Dict with capability information
|
|
401
|
+
"""
|
|
402
|
+
return {
|
|
403
|
+
"library": "nodriver",
|
|
404
|
+
"architecture": "async-first",
|
|
405
|
+
"features": {
|
|
406
|
+
"no_external_chromedriver": True,
|
|
407
|
+
"built_in_stealth": True,
|
|
408
|
+
"async_operations": True,
|
|
409
|
+
"devtools_protocol": True,
|
|
410
|
+
"automatic_updates": True,
|
|
411
|
+
"headless_support": True,
|
|
412
|
+
"headed_mode_recommended": True,
|
|
413
|
+
"memory_efficient": True
|
|
414
|
+
},
|
|
415
|
+
"bypass_capabilities": {
|
|
416
|
+
"cloudflare": True,
|
|
417
|
+
"datadome": True,
|
|
418
|
+
"imperva": True,
|
|
419
|
+
"botd": "advanced",
|
|
420
|
+
"recaptcha": True,
|
|
421
|
+
"advanced_fingerprinting": True,
|
|
422
|
+
"waf_bypass": True
|
|
423
|
+
},
|
|
424
|
+
"advantages": [
|
|
425
|
+
"No ChromeDriver dependency",
|
|
426
|
+
"Better performance with async operations",
|
|
427
|
+
"Built-in stealth capabilities",
|
|
428
|
+
"Regular updates and maintenance",
|
|
429
|
+
"Advanced DevTools Protocol usage"
|
|
430
|
+
],
|
|
431
|
+
"description": "Next-generation bot detection bypass with async architecture"
|
|
432
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PlaywrightStealth - Integration with playwright-stealth 2.0.0
|
|
3
|
+
|
|
4
|
+
Provides integration with playwright-stealth library for detection bypass:
|
|
5
|
+
- Configuration of all available stealth options
|
|
6
|
+
- Application to pages and contexts
|
|
7
|
+
- Error handling and fallback
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from playwright.async_api import Page, BrowserContext
|
|
12
|
+
|
|
13
|
+
# CRITICAL REQUIREMENTS COMPLIANCE - NO INLINE IMPORTS!
|
|
14
|
+
from playwright_stealth import Stealth
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
logger.setLevel(logging.DEBUG)
|
|
18
|
+
|
|
19
|
+
class PlaywrightStealth:
|
|
20
|
+
"""
|
|
21
|
+
Integration with playwright-stealth 2.0.0
|
|
22
|
+
|
|
23
|
+
Provides application of all available stealth techniques
|
|
24
|
+
from playwright-stealth library for maximum detection bypass
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, logger_bridge=None):
|
|
28
|
+
"""Initialize PlaywrightStealth"""
|
|
29
|
+
self.logger_bridge = logger_bridge
|
|
30
|
+
self.stealth_config = self._create_stealth_config()
|
|
31
|
+
|
|
32
|
+
def _logger(self, message: str, level: str = "info") -> None:
|
|
33
|
+
"""Private wrapper for logger with bridge support"""
|
|
34
|
+
if self.logger_bridge:
|
|
35
|
+
if level == "info":
|
|
36
|
+
self.logger_bridge.log_info(message)
|
|
37
|
+
elif level == "error":
|
|
38
|
+
self.logger_bridge.log_error(message)
|
|
39
|
+
elif level == "warning":
|
|
40
|
+
self.logger_bridge.log_warning(message)
|
|
41
|
+
else:
|
|
42
|
+
self.logger_bridge.log_info(message)
|
|
43
|
+
else:
|
|
44
|
+
if level == "info":
|
|
45
|
+
logger.info(message)
|
|
46
|
+
elif level == "error":
|
|
47
|
+
logger.error(message)
|
|
48
|
+
elif level == "warning":
|
|
49
|
+
logger.warning(message)
|
|
50
|
+
else:
|
|
51
|
+
logger.info(message)
|
|
52
|
+
|
|
53
|
+
def _create_stealth_config(self) -> Stealth:
|
|
54
|
+
"""
|
|
55
|
+
Create comprehensive stealth configuration
|
|
56
|
+
Enables ALL available stealth features for maximum protection
|
|
57
|
+
"""
|
|
58
|
+
return Stealth(
|
|
59
|
+
# 🔥 CRITICAL: Core webdriver removal
|
|
60
|
+
navigator_webdriver=True, # Remove navigator.webdriver property
|
|
61
|
+
|
|
62
|
+
# 🎭 Chrome object spoofing
|
|
63
|
+
chrome_runtime=True, # Enable chrome runtime spoofing
|
|
64
|
+
chrome_app=True, # Enable chrome app spoofing
|
|
65
|
+
chrome_csi=True, # Enable chrome CSI spoofing
|
|
66
|
+
chrome_load_times=True, # Enable load times spoofing
|
|
67
|
+
|
|
68
|
+
# 🌐 Navigator spoofing
|
|
69
|
+
navigator_languages=True, # Spoof languages
|
|
70
|
+
navigator_permissions=True, # Spoof permissions
|
|
71
|
+
navigator_plugins=True, # Spoof plugins
|
|
72
|
+
navigator_user_agent=True, # Spoof user agent - CRITICAL for headless
|
|
73
|
+
navigator_vendor=True, # Spoof navigator vendor
|
|
74
|
+
navigator_platform=True, # Spoof platform
|
|
75
|
+
navigator_hardware_concurrency=True, # Spoof hardware concurrency
|
|
76
|
+
|
|
77
|
+
# 🎨 WebGL and Canvas spoofing
|
|
78
|
+
webgl_vendor=True, # Spoof WebGL vendor - CRITICAL for BotD
|
|
79
|
+
hairline=True, # Enable hairline spoofing - helps with canvas fingerprinting
|
|
80
|
+
|
|
81
|
+
# 🔧 Modern browser features
|
|
82
|
+
sec_ch_ua=True, # Enable sec-ch-ua spoofing - modern Chrome headers
|
|
83
|
+
iframe_content_window=True, # Enable iframe spoofing
|
|
84
|
+
media_codecs=True, # Enable media codecs spoofing
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
async def apply_stealth(self, page: Page) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
Apply playwright-stealth to page
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
page: Playwright page instance
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
True if successful, False otherwise
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
self._logger("🎭 Applying playwright-stealth 2.0.0...", "info")
|
|
99
|
+
|
|
100
|
+
# Apply stealth using new 2.0.0 API
|
|
101
|
+
await self.stealth_config.apply_stealth_async(page)
|
|
102
|
+
|
|
103
|
+
self._logger("✅ Playwright-stealth 2.0.0 applied successfully", "info")
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
except Exception as e:
|
|
107
|
+
self._logger(f"❌ Playwright-stealth failed: {e}", "error")
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
async def apply_stealth_to_context(self, context: BrowserContext) -> bool:
|
|
111
|
+
"""
|
|
112
|
+
Apply stealth to browser context
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
context: Playwright browser context
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
True if successful, False otherwise
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
self._logger("🎭 Applying playwright-stealth to context...", "info")
|
|
122
|
+
|
|
123
|
+
# For context, we need to apply stealth to each new page
|
|
124
|
+
# This is handled by the context's page creation events
|
|
125
|
+
|
|
126
|
+
# Add init script for basic webdriver removal at context level
|
|
127
|
+
webdriver_removal_script = """
|
|
128
|
+
// Basic webdriver removal at context level
|
|
129
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
130
|
+
get: () => undefined,
|
|
131
|
+
configurable: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Remove from prototype
|
|
135
|
+
if (navigator.webdriver !== undefined) {
|
|
136
|
+
delete Object.getPrototypeOf(navigator).webdriver;
|
|
137
|
+
}
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
await context.add_init_script(webdriver_removal_script)
|
|
141
|
+
|
|
142
|
+
self._logger("✅ Playwright-stealth context setup completed", "info")
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
self._logger(f"❌ Playwright-stealth context setup failed: {e}", "error")
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
def get_stealth_info(self) -> dict:
|
|
150
|
+
"""
|
|
151
|
+
Get information about current stealth configuration
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Dict with stealth configuration details
|
|
155
|
+
"""
|
|
156
|
+
return {
|
|
157
|
+
"library": "playwright-stealth",
|
|
158
|
+
"version": "2.0.0",
|
|
159
|
+
"features_enabled": {
|
|
160
|
+
"navigator_webdriver": True,
|
|
161
|
+
"chrome_runtime": True,
|
|
162
|
+
"chrome_app": True,
|
|
163
|
+
"chrome_csi": True,
|
|
164
|
+
"chrome_load_times": True,
|
|
165
|
+
"navigator_languages": True,
|
|
166
|
+
"navigator_permissions": True,
|
|
167
|
+
"navigator_plugins": True,
|
|
168
|
+
"navigator_user_agent": True,
|
|
169
|
+
"navigator_vendor": True,
|
|
170
|
+
"navigator_platform": True,
|
|
171
|
+
"navigator_hardware_concurrency": True,
|
|
172
|
+
"webgl_vendor": True,
|
|
173
|
+
"hairline": True,
|
|
174
|
+
"sec_ch_ua": True,
|
|
175
|
+
"iframe_content_window": True,
|
|
176
|
+
"media_codecs": True,
|
|
177
|
+
},
|
|
178
|
+
"description": "Comprehensive stealth configuration with all features enabled"
|
|
179
|
+
}
|