Transplanted from puppeteer-extra-plugin-stealth, with some improvements. Don't expect this to bypass anything but the simplest of bot detection methods. Consider this a proof-of-concept starting point.
This is a WIP fork. I've merged some of the outstanding PRs and added some features with the goal of replacing the out-of-date upstream. See the changelog.
Install the latest stable release:
$ pip install git+https://github.com/Mattwmaster58/playwright_stealth@rc3
or straight from main
$ pip install git+https://github.com/Mattwmaster58/playwright_stealth
import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import Stealth, ALL_EVASIONS_DISABLED_KWARGS
async def main():
# This is the recommended usage. All pages created will have stealth applied:
async with Stealth().use_async(async_playwright()) as p:
# or, to hook every browser launched from this context: stealth.hook_playwright_context(p)
browser = await p.chromium.launch()
page = await browser.new_page()
print("from new_page: ", await page.evaluate("navigator.webdriver"))
different_context = await browser.new_context()
page_from_different_context = await different_context.new_page()
print("from new_context: ", await page_from_different_context.evaluate("navigator.webdriver"))
# Specifying config options and applying evasions manually to an entire context:
custom_languages = ("fr-FR", "fr")
stealth = Stealth(
navigator_languages_override=custom_languages,
init_scripts_only=True
)
async with async_playwright() as p:
browser = await p.chromium.launch()
# this isn't, certain evasions will work worse, and some won't be able to be applied at all
context = await browser.new_context()
await stealth.apply_stealth_async(context)
page_1 = await context.new_page()
concurrency_on_page_1_mocked = await page_1.evaluate("navigator.languages") == custom_languages
print("manually applied stealth applied to page 1:", concurrency_on_page_1_mocked)
page_2 = await context.new_page()
concurrency_on_page_2_mocked = await page_2.evaluate("navigator.languages") == custom_languages
print("manually applied stealth applied to page 2:", concurrency_on_page_2_mocked)
# a constant "ALL_EVASIONS_DISABLED_KWARGS" is provided if only a few evasions are desired:
assert len(Stealth(**ALL_EVASIONS_DISABLED_KWARGS).script_payload) == 0
# all but navigator_webdriver disabled
assert len(Stealth(**{**ALL_EVASIONS_DISABLED_KWARGS, "navigator_webdriver": True}).script_payload) > 0
asyncio.run(main())
A set of