How to Configure CDP Mode for Stealth Browsing in MediaCrawler
MediaCrawler supports stealth browsing via Chrome DevTools Protocol (CDP) by connecting to a remote debugging endpoint and injecting anti-fingerprinting scripts from libs/stealth.min.js into every page context.
MediaCrawler is an open-source scraping framework that leverages Playwright to automate browser interactions. When you configure CDP mode for stealth browsing in MediaCrawler, the tool connects directly to Chrome or Edge via the Chrome DevTools Protocol, bypassing standard automation detection while maintaining full control over the browser instance.
Enable CDP Mode in Configuration
All CDP behavior is controlled through the configuration module defined in config/base_config.py. The following flags determine how the browser launches and maintains its connection.
CDP_CONNECT_EXISTING: Set toTrueto attach to an already-running Chrome instance with--remote-debugging-portenabled, orFalseto let MediaCrawler launch a fresh browser process.CDP_DEBUG_PORT: The TCP port (default9222) where the browser’s CDP server listens.CUSTOM_BROWSER_PATH: Absolute path to a Chrome/Edge binary; if empty, the launcher auto-detects installed browsers.SAVE_LOGIN_STATE: WhenTrue, creates a persistent user-data directory so cookies and local storage survive restarts.BROWSER_LAUNCH_TIMEOUT: Seconds to wait for the browser to become reachable (default30).AUTO_CLOSE_BROWSER: Whether to terminate the browser process on script exit (True) or leave it running (False).
These values are read in tools/cdp_browser.py, where the decision to attach versus launch is made on lines 108-110.
Launch or Attach to a CDP Browser Instance
The CDPBrowserManager class in tools/cdp_browser.py handles the lifecycle of the CDP connection. Use the launch_and_connect() method to establish the session according to your configuration.
Launching a Fresh Browser Instance
When CDP_CONNECT_EXISTING is False, MediaCrawler spawns a new browser process and waits for the CDP endpoint.
from tools.cdp_browser import CDPBrowserManager
from playwright.async_api import async_playwright
async def start_fresh():
async with async_playwright() as pw:
manager = CDPBrowserManager()
context = await manager.launch_and_connect(
playwright=pw,
playwright_proxy=None,
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
headless=False, # Set True in production
)
# Connection established (see lines [97-104](https://github.com/NanmiCoder/MediaCrawler/blob/main/tools/cdp_browser.py#L97-L104))
return manager, context
Attaching to an Existing Chrome Process
Set CDP_CONNECT_EXISTING to True and start Chrome manually with the remote debugging flag:
chrome --remote-debugging-port=9222
Then connect via Python:
async def attach_existing():
async with async_playwright() as pw:
manager = CDPBrowserManager()
# In practice, set this via config or before calling launch_and_connect
manager.manager_config = {"CDP_CONNECT_EXISTING": True}
context = await manager.launch_and_connect(playwright=pw, headless=False)
# Manager waits for endpoint (see lines [166-176](https://github.com/NanmiCoder/MediaCrawler/blob/main/tools/cdp_browser.py#L166-L176))
return manager, context
Inject the Stealth Script
After establishing the context, inject the anti-detection logic. MediaCrawler ships with a pre-minified stealth script located at libs/stealth.min.js.
Call add_stealth_script() to register the script as an init script for every new page:
await manager.add_stealth_script() # Uses default libs/stealth.min.js
# Or specify a custom path:
# await manager.add_stealth_script("custom_stealth.js")
This method verifies the file exists and registers it via page.addInitScript so it executes before any page scripts run (see lines 400-407).
Complete Working Example
Combine the configuration, launch, and stealth injection into a single workflow:
import asyncio
from tools.cdp_browser import CDPBrowserManager
from playwright.async_api import async_playwright
async def run_stealth_crawl():
async with async_playwright() as pw:
mgr = CDPBrowserManager()
# Launch CDP browser (respects settings in base_config.py)
ctx = await mgr.launch_and_connect(
playwright=pw,
headless=False,
)
# Enable stealth browsing
await mgr.add_stealth_script()
# Begin crawling
page = await ctx.new_page()
await page.goto("https://www.zhihu.com")
# Cleanup
await mgr.cleanup()
asyncio.run(run_stealth_crawl())
Persist Browser State Across Sessions
When SAVE_LOGIN_STATE is enabled, the manager creates a directory at browser_data/cdp_<platform> (see lines 55-63). This folder stores cookies, local storage, and extension data, allowing you to maintain login sessions across crawler restarts.
# Ensure in config/base_config.py:
# SAVE_LOGIN_STATE = True
# The manager automatically uses this path when creating the context
ctx = await mgr.launch_and_connect(playwright=pw)
Summary
- Configuration: Set
CDP_CONNECT_EXISTING,CDP_DEBUG_PORT, and related flags inconfig/base_config.pyto control launch behavior. - Connection: Use
CDPBrowserManager.launch_and_connect()intools/cdp_browser.pyto attach to or spawn a CDP-enabled browser. - Stealth: Call
add_stealth_script()to injectlibs/stealth.min.jsand mask automation fingerprints. - Persistence: Enable
SAVE_LOGIN_STATEto retain cookies inbrowser_data/cdp_<platform>for long-running crawls.
Frequently Asked Questions
What is CDP mode in MediaCrawler?
CDP mode drives Chrome or Edge via the Chrome DevTools Protocol instead of standard Playwright browser management. This allows MediaCrawler to attach to existing browser instances, use custom user-data directories, and inject stealth scripts at the CDP level to evade bot detection.
How does the stealth script prevent detection?
The script in libs/stealth.min.js overrides JavaScript properties that automation frameworks typically expose—such as navigator.webdriver, window.chrome, and plugin inconsistencies. By executing as an init script (lines 400-407), it sanitizes the browser environment before any page scripts run.
Can I use a custom stealth script instead of the default?
Yes. Pass the absolute path to your custom script file when calling await manager.add_stealth_script("path/to/custom.js"). The method validates the file existence and registers it as the init script for all subsequent pages.
Why should I use CDP mode instead of standard Playwright?
CDP mode provides lower-level control over the browser process, enables connection to already-running instances (useful for debugging), and supports persistent user-data directories that standard Playwright contexts may handle differently. It also facilitates stealth configurations that are harder to achieve through high-level browser APIs alone.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →