feat: switch stealth Chromium default channel to consumer Chrome
CI / test (push) Successful in 2m16s
CI / build (push) Successful in 2m25s
CI / vet (push) Successful in 2m16s

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:
2026-04-22 17:26:50 +00:00
parent 9987b94233
commit c3be14095a
3 changed files with 32 additions and 2 deletions
+14 -2
View File
@@ -104,8 +104,17 @@ func initBrowser(opt BrowserOptions) (*browserInitResult, error) {
if len(launchArgs) > 0 {
launchOpts.Args = launchArgs
}
if stealth && opt.Browser == BrowserChromium && headless {
launchOpts.Channel = playwright.String("chromium")
if opt.Browser == BrowserChromium {
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)
if err != nil {
@@ -216,6 +225,9 @@ func mergeOptions(base BrowserOptions, opts []BrowserOptions) BrowserOptions {
if o.Stealth != nil {
base.Stealth = o.Stealth
}
if o.Channel != "" {
base.Channel = o.Channel
}
}
return base
}