# -*- coding: utf-8 -*-
import json
from dataclasses import dataclass
from typing import Tuple, Optional, Dict
from playwright.async_api import Page as AsyncPage
from .js.chrome_app import chrome_app
from .js.chrome_csi import chrome_csi
from .js.chrome_hairline import chrome_hairline
from .js.chrome_load_times import chrome_load_times
from .js.chrome_runtime import chrome_runtime
from .js.generate_magic_arrays import generate_magic_arrays
from .js.iframe_contentWindow import iframe_contentWindow
from .js.media_codecs import media_codecs
from .js.navigator_hardwareConcurrency import navigator_hardwareConcurrency
from .js.navigator_languages import navigator_languages
from .js.navigator_permissions import navigator_permissions
from .js.navigator_platform import navigator_platform
from .js.navigator_plugins import navigator_plugins
from .js.navigator_userAgent import navigator_userAgent
from .js.navigator_vendor import navigator_vendor
from .js.webgl_vendor import webgl_vendor
from .js.window_outerdimensions import window_outerdimensions
from .js.utils import utils
SCRIPTS: Dict[str, str] = {
"chrome_csi": chrome_csi,
"chrome_app": chrome_app,
"chrome_runtime": chrome_runtime,
"chrome_load_times": chrome_load_times,
"chrome_hairline": chrome_hairline,
"generate_magic_arrays": generate_magic_arrays,
"iframe_content_window": iframe_contentWindow,
"media_codecs": media_codecs,
"navigator_vendor": navigator_vendor,
"navigator_plugins": navigator_plugins,
"navigator_permissions": navigator_permissions,
"navigator_languages": navigator_languages,
"navigator_platform": navigator_platform,
"navigator_user_agent": navigator_userAgent,
"navigator_hardware_concurrency": navigator_hardwareConcurrency,
"outerdimensions": window_outerdimensions,
"utils": utils,
"webdriver": "delete Object.getPrototypeOf(navigator).webdriver",
"webgl_vendor": webgl_vendor,
}
[docs]
@dataclass
class StealthConfig:
"""
Playwright stealth configuration that applies stealth strategies to playwright page objects.
The stealth strategies are contained in ./js package and are basic javascript scripts that are executed
on every page.goto() called.
Note:
All init scripts are combined by playwright into one script and then executed this means
the scripts should not have conflicting constants/variables etc. !
This also means scripts can be extended by overriding enabled_scripts generator:
```
@property
def enabled_scripts():
yield 'console.log("first script")'
yield from super().enabled_scripts()
yield 'console.log("last script")'
```
"""
# load script options
webdriver: bool = True
webgl_vendor: bool = True
chrome_app: bool = True
chrome_csi: bool = True
chrome_load_times: bool = True
chrome_runtime: bool = True
iframe_content_window: bool = True
media_codecs: bool = True
navigator_hardware_concurrency: int = 4
navigator_languages: bool = True
navigator_permissions: bool = True
navigator_platform: bool = True
navigator_plugins: bool = True
navigator_user_agent: bool = True
navigator_vendor: bool = True
outerdimensions: bool = True
hairline: bool = True
# options
vendor: str = "Intel Inc."
renderer: str = "Intel Iris OpenGL Engine"
nav_vendor: str = "Google Inc."
nav_user_agent: str = None
nav_platform: str = None
languages: Tuple[str] = ("en-US", "en")
runOnInsecureOrigins: Optional[bool] = None
@property
def enabled_scripts(self):
opts = json.dumps(
{
"webgl_vendor": self.vendor,
"webgl_renderer": self.renderer,
"navigator_vendor": self.nav_vendor,
"navigator_platform": self.nav_platform,
"navigator_user_agent": self.nav_user_agent,
"languages": list(self.languages),
"runOnInsecureOrigins": self.runOnInsecureOrigins,
}
)
# defined options constant
yield f"const opts = {opts}"
# init utils and generate_magic_arrays helper
yield SCRIPTS["utils"]
yield SCRIPTS["generate_magic_arrays"]
if self.chrome_app:
yield SCRIPTS["chrome_app"]
if self.chrome_csi:
yield SCRIPTS["chrome_csi"]
if self.hairline:
yield SCRIPTS["chrome_hairline"]
if self.chrome_load_times:
yield SCRIPTS["chrome_load_times"]
if self.chrome_runtime:
yield SCRIPTS["chrome_runtime"]
if self.iframe_content_window:
yield SCRIPTS["iframe_content_window"]
if self.media_codecs:
yield SCRIPTS["media_codecs"]
if self.navigator_languages:
yield SCRIPTS["navigator_languages"]
if self.navigator_permissions:
yield SCRIPTS["navigator_permissions"]
if self.navigator_platform:
yield SCRIPTS["navigator_platform"]
if self.navigator_plugins:
yield SCRIPTS["navigator_plugins"]
if self.navigator_user_agent:
yield SCRIPTS["navigator_user_agent"]
if self.navigator_vendor:
yield SCRIPTS["navigator_vendor"]
if self.webdriver:
yield SCRIPTS["webdriver"]
if self.outerdimensions:
yield SCRIPTS["outerdimensions"]
if self.webgl_vendor:
yield SCRIPTS["webgl_vendor"]
[docs]
async def stealth_async(page: AsyncPage, config: StealthConfig = None):
"""stealth the page"""
for script in (config or StealthConfig()).enabled_scripts:
await page.add_init_script(script)