Playwright Stealth Configuration
Configure Playwright with stealth techniques to bypass common anti-bot detection systems like Cloudflare and DataDome.
Modern anti-bot systems detect headless browsers by checking navigator properties, WebGL fingerprints, and CDP (Chrome DevTools Protocol) artifacts. This snippet configures Playwright with stealth settings that mask the most common detection vectors, giving you a browser context that looks like a real user.
from playwright.async_api import async_playwrightimport asyncio
async def create_stealth_browser(): """Launch a stealth-configured Playwright browser.""" pw = await async_playwright().start()
browser = await pw.chromium.launch( headless=True, args=[ "--disable-blink-features=AutomationControlled", # Remove automation flag "--disable-features=IsolateOrigins,site-per-process", "--disable-site-isolation-trials", "--no-first-run", "--no-default-browser-check", ], )
# Create context with realistic viewport and user agent context = await browser.new_context( viewport={"width": 1920, "height": 1080}, user_agent=( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/131.0.0.0 Safari/537.36" ), locale="en-US", timezone_id="America/New_York", color_scheme="light", java_script_enabled=True, )
# Inject stealth scripts before every page load await context.add_init_script(""" // Override navigator.webdriver to return undefined Object.defineProperty(navigator, 'webdriver', { get: () => undefined, });
// Override chrome.runtime to mimic real Chrome window.chrome = { runtime: { connect: () => {}, sendMessage: () => {} }, };
// Fix permissions API to match real browser behavior const originalQuery = window.navigator.permissions.query; window.navigator.permissions.query = (parameters) => parameters.name === 'notifications' ? Promise.resolve({ state: Notification.permission }) : originalQuery(parameters);
// Spoof plugins array (headless Chrome has empty plugins) Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5], });
// Spoof languages Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], }); """)
return pw, browser, context
async def scrape_protected_page(url: str) -> str: """Scrape a page protected by anti-bot systems.""" pw, browser, context = await create_stealth_browser() try: page = await context.new_page() # Navigate with realistic timeout await page.goto(url, wait_until="networkidle", timeout=30000) # Wait for dynamic content to load await page.wait_for_timeout(2000) content = await page.content() return content finally: await browser.close() await pw.stop()
# Usageif __name__ == "__main__": html = asyncio.run(scrape_protected_page("https://example.com")) print(f"Page length: {len(html)} characters")When to use this
Use Playwright with stealth configuration when you’re scraping sites protected by Cloudflare, DataDome, PerimeterX, or similar anti-bot systems that block standard headless browsers. This approach is heavier than plain HTTP requests — only use it when simpler methods fail. Consider adding random delays, mouse movements, and scroll actions for sites with behavioral analysis.