feat: switch stealth Chromium default channel to consumer Chrome
Playwright's bundled Chromium has a distinct build fingerprint (build ID, uniform WebGL/codec lists, HeadlessChrome residue) that anti-bot services increasingly flag. Driving a system-installed Google Chrome via Playwright's channel option sheds that signal and aligns sec-ch-ua with UA more cleanly. Changes: - Add BrowserOptions.Channel string field (chrome, chrome-beta, chromium, msedge; empty = default). - When stealth+headless+Chromium and Channel is empty, default to "chrome" (was "chromium"). Explicit Channel values always win, so callers can opt back to "chromium" or pick another channel. - Merge Channel in mergeOptions. - Expose --channel/--ch flag on cmd/browser for A/B fingerprint testing. Callers must have the chosen browser installed on the host (e.g. `playwright install chrome`). Firefox and WebKit paths are untouched. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+14
-2
@@ -104,8 +104,17 @@ func initBrowser(opt BrowserOptions) (*browserInitResult, error) {
|
|||||||
if len(launchArgs) > 0 {
|
if len(launchArgs) > 0 {
|
||||||
launchOpts.Args = launchArgs
|
launchOpts.Args = launchArgs
|
||||||
}
|
}
|
||||||
if stealth && opt.Browser == BrowserChromium && headless {
|
if opt.Browser == BrowserChromium {
|
||||||
launchOpts.Channel = playwright.String("chromium")
|
channel := opt.Channel
|
||||||
|
if channel == "" && stealth && headless {
|
||||||
|
// Real Chrome sheds Playwright's bundled-Chromium fingerprint
|
||||||
|
// (build ID, uniform WebGL, HeadlessChrome residue) that
|
||||||
|
// anti-bot services increasingly flag.
|
||||||
|
channel = "chrome"
|
||||||
|
}
|
||||||
|
if channel != "" {
|
||||||
|
launchOpts.Channel = playwright.String(channel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
browser, err = bt.Launch(launchOpts)
|
browser, err = bt.Launch(launchOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -216,6 +225,9 @@ func mergeOptions(base BrowserOptions, opts []BrowserOptions) BrowserOptions {
|
|||||||
if o.Stealth != nil {
|
if o.Stealth != nil {
|
||||||
base.Stealth = o.Stealth
|
base.Stealth = o.Stealth
|
||||||
}
|
}
|
||||||
|
if o.Channel != "" {
|
||||||
|
base.Channel = o.Channel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ var Flags = BrowserFlags{
|
|||||||
Usage: "Disable stealth mode (anti-bot-detection evasions are enabled by default)",
|
Usage: "Disable stealth mode (anti-bot-detection evasions are enabled by default)",
|
||||||
DefaultText: "false",
|
DefaultText: "false",
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "channel",
|
||||||
|
Usage: "Chromium channel to launch (chrome, chrome-beta, chromium, msedge). Only applies when --browser=chromium. Empty = stealth default (chrome).",
|
||||||
|
Aliases: []string{"ch"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromCommand(ctx context.Context, cmd *cli.Command) (extractor.Browser, error) {
|
func FromCommand(ctx context.Context, cmd *cli.Command) (extractor.Browser, error) {
|
||||||
@@ -83,5 +88,9 @@ func FromCommand(ctx context.Context, cmd *cli.Command) (extractor.Browser, erro
|
|||||||
opts.Stealth = extractor.Bool(false)
|
opts.Stealth = extractor.Bool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ch := cmd.String("channel"); ch != "" {
|
||||||
|
opts.Channel = ch
|
||||||
|
}
|
||||||
|
|
||||||
return extractor.NewBrowser(ctx, opts)
|
return extractor.NewBrowser(ctx, opts)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,15 @@ type BrowserOptions struct {
|
|||||||
// evasions are applied automatically (launch args + init scripts). When nil,
|
// evasions are applied automatically (launch args + init scripts). When nil,
|
||||||
// defaults to true in NewBrowser / NewInteractiveBrowser.
|
// defaults to true in NewBrowser / NewInteractiveBrowser.
|
||||||
Stealth *bool
|
Stealth *bool
|
||||||
|
|
||||||
|
// Channel selects a Chromium-family browser channel when Browser is
|
||||||
|
// BrowserChromium. Accepted values include "chrome", "chrome-beta",
|
||||||
|
// "chromium", "msedge". Empty defaults to "chrome" when stealth+headless
|
||||||
|
// (real Chrome sheds the bundled-Chromium fingerprint); otherwise falls
|
||||||
|
// back to Playwright's bundled browser. The chosen channel must be
|
||||||
|
// installed on the host (e.g. `playwright install chrome`). Ignored when
|
||||||
|
// Browser is not BrowserChromium.
|
||||||
|
Channel string
|
||||||
}
|
}
|
||||||
|
|
||||||
func sameSiteToPlaywright(s SameSite) *playwright.SameSiteAttribute {
|
func sameSiteToPlaywright(s SameSite) *playwright.SameSiteAttribute {
|
||||||
|
|||||||
Reference in New Issue
Block a user