Metadata-Version: 2.4
Name: playwright-stealth
Version: 2.0.3
Summary: Make your playwright instance stealthy
License-Expression: MIT
License-File: LICENSE
Author: Mattwmaster58
Author-email: mattwmaster58@gmail.com>
Requires-Python: >=3.9
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Provides-Extra: black
Requires-Dist: black (>=25.9.0) ; extra == "black"
Requires-Dist: playwright (>=1.40.0)
Project-URL: Homepage, https://github.com/Mattwmaster58/playwright_stealth
Project-URL: changelog, https://github.com/Mattwmaster58/playwright_stealth/raw/main/CHANGELOG.md
Description-Content-Type: text/markdown
# playwright_stealth
Fork of AtuboDad's port
of [puppeteer-extra-plugin-stealth](https://github.com/berstend/puppeteer-extra/tree/master/packages/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.
I've merged some of the outstanding PRs, added some features, and cleaned up the API surface. See
the [changelog](https://github.com/Mattwmaster58/playwright_stealth/blob/main/CHANGELOG.md). The latest major version includes breaking changes.
## Install
Install from PyPi:
```
$ pip install playwright-stealth
```
## Example Usage
### Recommended Usage
```python
import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import Stealth
async def main():
# This is the recommended usage. All pages created will have stealth applied:
async with Stealth().use_async(async_playwright()) as p:
browser = await p.chromium.launch()
page = await browser.new_page()
webdriver_status = await page.evaluate("navigator.webdriver")
print("from new_page: ", webdriver_status)
different_context = await browser.new_context()
page_from_different_context = await different_context.new_page()
different_context_status = await page_from_different_context.evaluate("navigator.webdriver")
print("from new_context: ", different_context_status)
asyncio.run(main())
```
### Specifying config options and applying evasions manually to an entire context
```python
import asyncio
from playwright.async_api import async_playwright
from playwright_stealth import Stealth, ALL_EVASIONS_DISABLED_KWARGS
async def advanced_example():
# Custom configuration with specific languages
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()
context = await browser.new_context()
await stealth.apply_stealth_async(context)
# Test stealth on multiple pages
page_1 = await context.new_page()
page_2 = await context.new_page()
# Verify language settings
for i, page in enumerate([page_1, page_2], 1):
is_mocked = await page.evaluate("navigator.languages") == custom_languages
print(f"Stealth applied to page {i}: {is_mocked}")
# Example of selective evasion usage
no_evasions = Stealth(**ALL_EVASIONS_DISABLED_KWARGS)
single_evasion = Stealth(**{**ALL_EVASIONS_DISABLED_KWARGS, "navigator_webdriver": True})
print("Total evasions (none):", len(no_evasions.script_payload))
print("Total evasions (single):", len(single_evasion.script_payload))
asyncio.run(advanced_example())
```
## Todo
- make this work with playwright.launch_persistent_context
- the difficult because sometimes we sniff the UA if an override isn't provided, and this is difficult to do when
launch_persistent_context is launched
- sec-platform (we have navigator_platform)
- docs
## A set of Test results
### playwright with stealth

### playwright without stealth
