https://github.com/regseb/playwright-ghost
Playwright overlay with plugins.
https://github.com/regseb/playwright-ghost
ghost headless playwright polyfill
Last synced: 13 days ago
JSON representation
Playwright overlay with plugins.
- Host: GitHub
- URL: https://github.com/regseb/playwright-ghost
- Owner: regseb
- License: mit
- Created: 2022-04-21T19:15:01.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-10-30T20:43:23.000Z (about 1 year ago)
- Last Synced: 2025-01-31T00:32:54.422Z (9 months ago)
- Topics: ghost, headless, playwright, polyfill
- Language: JavaScript
- Homepage:
- Size: 1.67 MB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Security: .github/SECURITY.md
Awesome Lists containing this project
README
# Playwright-ghost

[![npm][img-npm]][link-npm] [![build][img-build]][link-build]
[![coverage][img-coverage]][link-coverage] [![semver][img-semver]][link-semver]
Playwright-ghost is an overlay on [Playwright](https://playwright.dev/), adding
plugins to conceal the differences between a browser used by a human being and a
[headless browser](https://en.wikipedia.org/wiki/Headless_browser) controlled by
a program.
The Playwright-ghost API is identical to that of Playwright, except for the
addition of the `plugins` option to the
[`BrowserType.launch([options])`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch)
and
[`BrowserType.launchPersistentContext(userDataDir, [options])`](https://playwright.dev/docs/api/class-browsertype#browser-type-launch-persistent-context)
methods.
The `plugins` property is an array containing the plugins to be added.
## Disclaimer
This project is not officially commissioned or supported by Microsoft and
Playwright.
## Install
[`playwright-ghost`](https://www.npmjs.com/package/playwright-ghost) doesn't
provide [`playwright`](https://www.npmjs.com/package/playwright), so you need to
add it to your dependencies.
```shell
npm install playwright playwright-ghost
```
`playwright-ghost` can also be used with
[`patchright`](https://www.npmjs.com/package/patchright) or
[`rebrowser-playwright`](https://www.npmjs.com/package/rebrowser-playwright).
```shell
npm install patchright playwright-ghost
npm install rebrowser-playwright playwright-ghost
```
## Use
Here's an example with the recommended plugins.
```javascript
import { chromium } from "playwright-ghost";
// Or to use patchright or rebrowser-playwright:
// import { chromium } from "playwright-ghost/patchright";
// import { chromium } from "playwright-ghost/rebrowser";
import plugins from "playwright-ghost/plugins";
const browser = await chromium.launch({
plugins: plugins.recommended(),
});
const context = await browser.newContext();
const page = await context.newPage();
await page.goto("https://example.com/");
const title = await page.locator("h1").textContent();
console.log(title);
await context.close();
await browser.close();
```
In this other example, three plugins are added:
- `polyfill.headless` has no options;
- `polyfill.screen` sets other values for screen size;
- `utils.adblocker` uses default options.
```javascript
import { chromium } from "playwright-ghost";
import plugins from "playwright-ghost/plugins";
const browser = await chromium.launch({
plugins: [
plugins.polyfill.headless(),
plugins.polyfill.screen({ width: 2560, height: 1440 }),
plugins.utils.adblocker(),
],
});
// ...
```
And for this example, the recommended plugins and the `utils.locale` plugin are
added.
```javascript
import { chromium } from "playwright-ghost";
import plugins from "playwright-ghost/plugins";
const browser = await chromium.launch({
plugins: [...plugins.recommended(), plugins.utils.locale()],
});
// ...
```
## Plugins
⭐️ is in [`recommended`](docs/plugins/recommended.md) / ⚙️ has options
### Polyfill
⭐️
⚙️
Name
Description
⭐️
polyfill.automation
Disable --enable-automation in Chromium.
⭐️
polyfill.headless
Correct many differences in JavaScript APIs between the headful and
headless versions of Chromium.
⭐️
⚙️
polyfill.screen
Set a realistic value for screen size: 1920x1080.
️
⚙️
polyfill.userAgent
Change the browser's user agent.
⭐️
⚙️
polyfill.viewport
Vary viewport size with random values between 1000x500 and 1800x800.
⭐️
polyfill.webdriver
Set navigator.webdriver to false.
polyfill.webGL
Modify WebGL parameter values.
### Humanize
⭐️
⚙️
Name
Description
⭐️
⚙️
humanize.click
Add delay between mousedown and mouseup for
clicks and double-clicks.
⭐️
⚙️
humanize.cursor
Move the cursor with human-like movements.
⭐️
⚙️
humanize.dialog
Close <dialog> within a humanly possible time (between
1 and 5 seconds).
### Utils
⭐️
⚙️
Name
Description
️⚙️
utils.adblocker
Add Ghostery adblocker.
️⚙️
utils.camoufox
Replace Firefox by Camoufox.
️⚙️
utils.debug
Add debugging to a page (transfer error; display cursor).
⚙️
utils.fingerprint
Change the browser fingerprint.
⚙️
utils.locale
Use the locally installed browser.
⚙️
utils.xvfb
Run browser in Xvfb (X Virtual Frame Buffer).
## Anti-bots
### Pass
This 20 anti-bots don't detect Playwright-ghost:
[Anubis](https://anubis.techaro.lol),
[Brotector](https://kaliiiiiiiiii.github.io/brotector/),
[BrowserScan](https://www.browserscan.net/bot-detection),
[Chromedriver Detector](https://hmaker.github.io/selenium-detector/),
[Detect CDP](https://bypassantibot.github.io/detectCDP/),
[Deviceandbrowserinfo](https://deviceandbrowserinfo.com/are_you_a_bot),
[Device Info](https://www.deviceinfo.me/)
[Disable-devtool](https://theajack.github.io/disable-devtool/),
[Fingerprint](https://fingerprint.com/products/bot-detection/),
[Fingerprint Pro Playground](https://demo.fingerprint.com/playground),
[Fingerprint-Scan](https://fingerprint-scan.com/),
[HeadlessDetectJS](https://github.com/LouisKlimek/HeadlessDetectJS),
[infosimples](https://infosimples.github.io/detect-headless/),
[Chrome Headless Detection (Intoli)](https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html),
[Check browser fingerprints (iphey)](https://iphey.com/),
[OverpoweredJS Fingerprinting Demo](https://overpoweredjs.com/demo.html),
[Pixelscan](https://pixelscan.net/fingerprint-check),
[Antibot (Sannysoft)](https://bot.sannysoft.com/),
[Simple Service Workers Fingerprinting Leaks Test](https://mihneamanolache.github.io/simple-sw-test/)
and
[Cloudflare turnstile demo](https://peet.ws/turnstile-test/non-interactive.html).
To find out which plugins are used, see the
[anti-bots integration tests](test/integration/antibots).
### Fail
This 3 anti-bots detect Playwright-ghost:
- [CreepJS](https://abrahamjuliot.github.io/creepjs/): _F_
- [rebrowser-bot-detector](https://bot-detector.rebrowser.net/):
_mainWorldExecution_, _pwInitScripts_ and _useragent_
- [Score detector (reCAPTCHA v3)](https://antcpt.com/score_detector/): _0.3_
Contributions are welcome to fix these defects.
## Customize
You can write your own plugins. A plugin is a function that returns an object
containing the hooks. The keys of this object are made up of the class, method
and hook type. For example:
- `"BrowserType.launch:before"`: modify the input arguments of the `launch()`
method of the `BrowserType` class.
- `"BrowserContext.newPage:after"`: modify the return parameter of the
`newPage()` method of the `BrowserContext` class.
The values of the object are functions applying the modifications.
- For `"before"` types, the function receives an array containing the arguments
of the hooked method. And it must return a new array containing the modified
arguments.
- For `"after"` types, the function receives the return value of the hooked
method. And it must return the modified return value.
```javascript
/// rickrollPlugin.js
export default function rickrollPlugin() {
return {
"BrowserType.launch:before": (args) => {
return [
{
...args[0],
args: ["--disable-volume-adjust-sound"],
},
];
},
"BrowserContext.newPage:after": (page) => {
page.addInitScript(() => {
// Execute script only in main frame.
if (window !== top) {
return;
}
addEventListener("load", () => {
const iframe = document.createElement("iframe");
iframe.src = "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ";
document.body.replaceChildren(iframe);
});
});
return page;
},
};
}
```
To use your plugin, add it to the `plugins` option.
```javascript
import { chromium } from "playwright-ghost";
import plugins from "playwright-ghost/plugins";
import rickrollPlugin from "./rickrollPlugin.js";
const browser = await chromium.launch({
plugins: [...plugins.recommended(), rickrollPlugin()],
});
// ...
```
This plugin isn't perfect, so
[let's see how we can improve it](docs/customize.md) (and also discover other
features).
[img-npm]:
https://img.shields.io/npm/dm/playwright-ghost?label=npm&logo=npm&logoColor=whitesmoke
[img-build]:
https://img.shields.io/github/actions/workflow/status/regseb/playwright-ghost/ci.yml?branch=main&logo=github&logoColor=whitesmoke
[img-coverage]:
https://img.shields.io/endpoint?label=coverage&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fregseb%2Fplaywright-ghost%2Fmain
[img-semver]:
https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&logoColor=whitesmoke
[link-npm]: https://www.npmjs.com/package/playwright-ghost
[link-build]:
https://github.com/regseb/playwright-ghost/actions/workflows/ci.yml?query=branch%3Amain
[link-coverage]:
https://dashboard.stryker-mutator.io/reports/github.com/regseb/playwright-ghost/main
[link-semver]: https://semver.org/spec/v2.0.0.html "Semantic Versioning 2.0.0"