170 lines
4.8 KiB
Python
170 lines
4.8 KiB
Python
"""Stealth configuration for browser fingerprint masking.
|
|
|
|
Uses playwright-stealth library to hide Playwright automation signatures.
|
|
"""
|
|
|
|
from typing import Optional
|
|
from playwright.sync_api import BrowserContext
|
|
from playwright.async_api import BrowserContext as AsyncBrowserContext
|
|
from pydantic import BaseModel, Field
|
|
from playwright_stealth import Stealth
|
|
|
|
|
|
class StealthConfig(BaseModel):
|
|
"""Configuration for browser stealth settings.
|
|
|
|
All options map to playwright-stealth evasion scripts.
|
|
Default values optimized for Zhihu anti-detection.
|
|
"""
|
|
|
|
stealth_enabled: bool = Field(
|
|
default=True,
|
|
description="Enable stealth mode (set False to disable)"
|
|
)
|
|
|
|
# Navigator evasion options
|
|
navigator_webdriver: bool = Field(
|
|
default=True,
|
|
description="Hide navigator.webdriver property (STEALTH-01)"
|
|
)
|
|
navigator_user_agent: bool = Field(
|
|
default=True,
|
|
description="Replace HeadlessChrome in User-Agent (STEALTH-04)"
|
|
)
|
|
navigator_plugins: bool = Field(
|
|
default=True,
|
|
description="Mock navigator.plugins array"
|
|
)
|
|
navigator_languages: bool = Field(
|
|
default=True,
|
|
description="Set navigator.languages"
|
|
)
|
|
navigator_platform: bool = Field(
|
|
default=True,
|
|
description="Set navigator.platform"
|
|
)
|
|
navigator_vendor: bool = Field(
|
|
default=True,
|
|
description="Set navigator.vendor"
|
|
)
|
|
navigator_permissions: bool = Field(
|
|
default=True,
|
|
description="Override navigator.permissions"
|
|
)
|
|
|
|
# Chrome object evasion (STEALTH-03)
|
|
chrome_app: bool = Field(
|
|
default=True,
|
|
description="Mock chrome.app object"
|
|
)
|
|
chrome_csi: bool = Field(
|
|
default=True,
|
|
description="Mock chrome.csi function"
|
|
)
|
|
chrome_load_times: bool = Field(
|
|
default=True,
|
|
description="Mock chrome.loadTimes function"
|
|
)
|
|
|
|
# WebGL and rendering
|
|
webgl_vendor: bool = Field(
|
|
default=True,
|
|
description="Override WebGL vendor/renderer strings"
|
|
)
|
|
|
|
# Other evasion options
|
|
hairline: bool = Field(
|
|
default=True,
|
|
description="Fix hairline detection"
|
|
)
|
|
iframe_content_window: bool = Field(
|
|
default=True,
|
|
description="Fix iframe contentWindow"
|
|
)
|
|
media_codecs: bool = Field(
|
|
default=True,
|
|
description="Mock media codecs"
|
|
)
|
|
error_prototype: bool = Field(
|
|
default=True,
|
|
description="Fix error prototype chain"
|
|
)
|
|
sec_ch_ua: bool = Field(
|
|
default=True,
|
|
description="Set sec-ch-ua headers"
|
|
)
|
|
|
|
def create_stealth(self) -> Stealth:
|
|
"""Create playwright-stealth Stealth instance from config.
|
|
|
|
Returns:
|
|
Configured Stealth instance
|
|
"""
|
|
return Stealth(
|
|
navigator_webdriver=self.navigator_webdriver,
|
|
navigator_user_agent=self.navigator_user_agent,
|
|
navigator_plugins=self.navigator_plugins,
|
|
navigator_languages=self.navigator_languages,
|
|
navigator_platform=self.navigator_platform,
|
|
navigator_vendor=self.navigator_vendor,
|
|
navigator_permissions=self.navigator_permissions,
|
|
chrome_app=self.chrome_app,
|
|
chrome_csi=self.chrome_csi,
|
|
chrome_load_times=self.chrome_load_times,
|
|
webgl_vendor=self.webgl_vendor,
|
|
hairline=self.hairline,
|
|
iframe_content_window=self.iframe_content_window,
|
|
media_codecs=self.media_codecs,
|
|
error_prototype=self.error_prototype,
|
|
sec_ch_ua=self.sec_ch_ua,
|
|
)
|
|
|
|
|
|
def apply_stealth_sync(
|
|
context: BrowserContext,
|
|
config: Optional[StealthConfig] = None
|
|
) -> None:
|
|
"""Apply stealth settings to browser context (synchronous).
|
|
|
|
Args:
|
|
context: Playwright BrowserContext to apply stealth to
|
|
config: StealthConfig instance (uses defaults if not provided)
|
|
"""
|
|
if config is None:
|
|
config = StealthConfig()
|
|
|
|
if not config.stealth_enabled:
|
|
return
|
|
|
|
stealth = config.create_stealth()
|
|
stealth.apply_stealth_sync(context)
|
|
|
|
|
|
async def apply_stealth_async(
|
|
context: AsyncBrowserContext,
|
|
config: Optional[StealthConfig] = None
|
|
) -> None:
|
|
"""Apply stealth settings to browser context (asynchronous).
|
|
|
|
Args:
|
|
context: Playwright async BrowserContext to apply stealth to
|
|
config: StealthConfig instance (uses defaults if not provided)
|
|
"""
|
|
if config is None:
|
|
config = StealthConfig()
|
|
|
|
if not config.stealth_enabled:
|
|
return
|
|
|
|
stealth = config.create_stealth()
|
|
await stealth.apply_stealth_async(context)
|
|
|
|
|
|
def get_default_stealth_config() -> StealthConfig:
|
|
"""Get default StealthConfig for Zhihu crawling.
|
|
|
|
Returns:
|
|
StealthConfig with all evasion options enabled
|
|
"""
|
|
return StealthConfig()
|