Code copied to clipboard!
python scraping Advanced

Playwright Stealth Configuration

Configure Playwright with stealth techniques to bypass common anti-bot detection systems like Cloudflare and DataDome.

By Mostafa

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_playwright
import 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()
# Usage
if __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.