Playwright-ghost is an overlay on Playwright, adding plugins to conceal the differences between a browser used by a human being and a 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])
and
BrowserType.launchPersistentContext(userDataDir, [options])
methods.
The plugins property is an array containing the plugins to be added.
This project is not officially commissioned or supported by Microsoft and Playwright.
playwright-ghost doesn't
provide playwright, so you need to
add it to your dependencies.
npm install playwright playwright-ghostplaywright-ghost can also be used with
patchright or
rebrowser-playwright.
npm install patchright playwright-ghost
npm install rebrowser-playwright playwright-ghostHere's an example with the recommended plugins.
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.headlesshas no options;polyfill.screensets other values for screen size;utils.adblockeruses default options.
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.
import { chromium } from "playwright-ghost";
import plugins from "playwright-ghost/plugins";
const browser = await chromium.launch({
plugins: [...plugins.recommended(), plugins.utils.locale()],
});
// ...⭐️ is in recommended / ⚙️ has options
| ⭐️ | ⚙️ | 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. |
| ⭐️ | ⚙️ | 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).
|
| ⭐️ | ⚙️ | Name | Description |
|---|---|---|---|
| ️⚙️ |
utils.adblocker
|
Add Ghostery adblocker. | |
| ️⚙️ |
utils.camoufox
|
Replace Firefox by Camoufox. | |
| ⚙️ |
utils.fingerprint
|
Change the browser fingerprint. | |
| ⚙️ |
utils.locale
|
Use the locally installed browser. | |
| ⚙️ |
utils.weston
|
Run browser in weston (a Wayland compositor). |
|
| ⚙️ |
utils.xvfb
|
Run browser in Xvfb (X Virtual Frame Buffer). |
| ⭐️ | ⚙️ | Name | Description |
|---|---|---|---|
| ️⚙️ |
debug.console
|
Display console messages and error from the browser in the program console. | |
debug.cursor
|
Show cursor in page. | ||
debug.sniffer
|
Monitor all JavaScript properties used in a page. |
This 24 anti-bots don't detect Playwright-ghost: Anubis, Bing, bounty-nodejs, Brotector, BrowserScan, Chromedriver Detector, Detect CDP, detectIncognito, Deviceandbrowserinfo, Device Info Disable-devtool, Fingerprint, Fingerprint Pro Playground, Fingerprint-Scan, HeadlessDetectJS, infosimples, Chrome Headless Detection (Intoli), Check browser fingerprints (iphey), OverpoweredJS Fingerprinting Demo, Pixelscan, rebrowser-bot-detector, Antibot (Sannysoft), Simple Service Workers Fingerprinting Leaks Test and Cloudflare turnstile demo.
To find out which plugins are used, see the anti-bots integration tests.
This 2 anti-bots detect Playwright-ghost:
- CreepJS: 44% like headless
- Score detector (reCAPTCHA v3): 0.3
Contributions are welcome to fix these defects.
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 thelaunch()method of theBrowserTypeclass."BrowserContext.newPage:after": modify the return parameter of thenewPage()method of theBrowserContextclass.
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.
/// 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.
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 (and also discover other features).