From 8b52007c65de184bd024cf1038faa399635f7258 Mon Sep 17 00:00:00 2001 From: Star Date: Wed, 23 Jul 2025 20:30:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E7=9A=84Chrome=E8=B0=83=E8=AF=95=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=EF=BC=88=E4=BE=8B=E5=A6=82docker=EF=BC=89=20=E6=9B=B4=E5=A5=BD?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81=E6=8C=89=E9=94=AE=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chrome.go | 50 ++++++--- http.go | 5 +- page.go | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 337 insertions(+), 20 deletions(-) diff --git a/chrome.go b/chrome.go index 481db6a..a0160fd 100644 --- a/chrome.go +++ b/chrome.go @@ -8,13 +8,16 @@ import ( "apigo.cc/gojs/goja" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" + "github.com/go-rod/rod/lib/launcher/flags" "github.com/ssgo/u" ) type Chrome struct { - id string - launcher *launcher.Launcher - browser *rod.Browser + id string + chromeURL string + chromePath string + launcher *launcher.Launcher + browser *rod.Browser } var chromes = map[string]*Chrome{} @@ -23,7 +26,8 @@ var chromesLock sync.Mutex func (ch *Chrome) Close(vm *goja.Runtime) { logger := gojs.GetLogger(vm) if ch.browser != nil { - logger.Info("关闭本地 Chrome 浏览器", "id", ch.id) + ver, _ := ch.browser.Version() + logger.Info("关闭Chrome浏览器", "id", ch.id, "browser", ver.Product, "userAgent", ver.UserAgent, "chromeURL", ch.chromeURL, "chromePath", ch.chromePath) ch.browser.Close() ch.browser = nil } @@ -44,7 +48,7 @@ func CloseAllChrome(vm *goja.Runtime) { ch.Close(vm) } logger := gojs.GetLogger(vm) - logger.Info("关闭所有 Chrome 浏览器", "count", n) + logger.Info("关闭所有未主动关闭的Chrome浏览器", "count", n) } } @@ -53,26 +57,48 @@ func StartChrome(showWindow *bool, vm *goja.Runtime) (*Chrome, error) { ch := &Chrome{} ch.id = u.UniqueId() ch.browser = rod.New() - if localBrowserPath, hasLocalBrowser := launcher.LookPath(); hasLocalBrowser { - ch.launcher = launcher.New().Bin(localBrowserPath).Headless(!u.Bool(showWindow)).Set("no-sandbox").Set("disable-gpu").Set("disable-dev-shm-usage").Set("single-process") + ch.chromeURL = conf.ChromeURL + if conf.ChromeURL == "" { + // 使用本地Chrome + ch.launcher = launcher.New() + if localBrowserPath, hasLocalBrowser := launcher.LookPath(); hasLocalBrowser { + ch.launcher.Bin(localBrowserPath) + ch.chromePath = localBrowserPath + } + // "--headless=new", "--no-sandbox", "--disable-dev-shm-usage", "--hide-scrollbars", "--font-render-hinting=none", "--disable-blink-features=AutomationControlled", "--disable-infobars", "--lang=zh-CN,zh", "--disable-extensions", "--disable-gpu", "--use-gl=swiftshader", "--ignore-gpu-blocklist", "--use-angle=swiftshader", "--disable-features=Translate" + ch.launcher.Headless(!u.Bool(showWindow)).Set("disable-dev-shm-usage").Set("single-process").Set("disable-blink-features", "AutomationControlled") + ch.launcher.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0") switch runtime.GOOS { case "linux": ch.launcher.Set("disable-setuid-sandbox") case "windows": ch.launcher.Set("disable-features=RendererCodeIntegrity") } - localChromeURL, err := ch.launcher.Launch() - if err != nil { + for _, opt := range conf.ChromeOption { + a := u.SplitTrimN(opt, "=", 2) + if len(a) == 2 { + ch.launcher.Set(flags.Flag(a[0]), a[1]) + } else { + ch.launcher.Set(flags.Flag(opt)) + } + } + if localChromeURL, err := ch.launcher.Launch(); err != nil { ch.Close(vm) return nil, gojs.Err(err) + } else { + ch.chromeURL = localChromeURL + if ch.chromePath == "" { + ch.chromePath = ch.launcher.Get(flags.Bin) + } } - logger.Info("启动本地 Chrome 浏览器", "id", ch.id, "url", localChromeURL, "path", localBrowserPath) - ch.browser.ControlURL(localChromeURL) } + + ch.browser.ControlURL(ch.chromeURL) if err := ch.browser.Connect(); err != nil { - ch.Close(vm) return nil, gojs.Err(err) } + ver, _ := ch.browser.Version() + logger.Info("启动Chrome浏览器", "id", ch.id, "browser", ver.Product, "userAgent", ver.UserAgent, "chromeURL", ch.chromeURL, "chromePath", ch.chromePath) chromesLock.Lock() chromes[ch.id] = ch diff --git a/http.go b/http.go index ba0bc0b..4266cca 100644 --- a/http.go +++ b/http.go @@ -33,8 +33,9 @@ var defaultHttp = &Http{ } var conf = struct { - Timeout int - ChromePath string + Timeout int + ChromeURL string + ChromeOption []string }{} func init() { diff --git a/page.go b/page.go index 3d201f3..12da1ed 100644 --- a/page.go +++ b/page.go @@ -60,7 +60,7 @@ func (pg *Page) Close() error { } func (pg *Page) GetResList() ([]Resource, error) { - r, err := proto.PageGetResourceTree{}.Call(pg.page) + r, err := proto.PageGetResourceTree{}.Call(pg.longTimeoutPage) if err != nil { return nil, gojs.Err(err) } @@ -381,9 +381,267 @@ func (pg *Page) Frame(selector string) (*Page, error) { return &Page{page: p}, nil } -// Page级 -func (pg *Page) Press(key string) error { - return gojs.Err(pg.page.Keyboard.Press(input.Key(key[0]))) +var isControlKey = map[string]bool{ + "ShiftLeft": true, + "ShiftRight": true, + "Shift": true, + "ControlLeft": true, + "ControlRight": true, + "Ctrl": true, + "Control": true, + "MetaLeft": true, + "MetaRight": true, + "Meta": true, + "Cmd": true, + "Windows": true, + "Win": true, + "AltLeft": true, + "AltRight": true, + "AltGraph": true, + "Option": true, + "Alt": true, +} + +var KeyMap = map[string]input.Key{ + "Escape": input.Escape, + "Esc": input.Escape, + "F1": input.F1, + "F2": input.F2, + "F3": input.F3, + "F4": input.F4, + "F5": input.F5, + "F6": input.F6, + "F7": input.F7, + "F8": input.F8, + "F9": input.F9, + "F10": input.F10, + "F11": input.F11, + "F12": input.F12, + "Backquote": input.Backquote, + "1": input.Digit1, + "2": input.Digit2, + "3": input.Digit3, + "4": input.Digit4, + "5": input.Digit5, + "6": input.Digit6, + "7": input.Digit7, + "8": input.Digit8, + "9": input.Digit9, + "0": input.Digit0, + "Digit1": input.Digit1, + "Digit2": input.Digit2, + "Digit3": input.Digit3, + "Digit4": input.Digit4, + "Digit5": input.Digit5, + "Digit6": input.Digit6, + "Digit7": input.Digit7, + "Digit8": input.Digit8, + "Digit9": input.Digit9, + "Digit0": input.Digit0, + "Minus": input.Minus, + "Equal": input.Equal, + "Backslash": input.Backslash, + "Backspace": input.Backspace, + "Tab": input.Tab, + "KeyQ": input.KeyQ, + "Q": input.KeyQ, + "KeyW": input.KeyW, + "W": input.KeyW, + "KeyE": input.KeyE, + "E": input.KeyE, + "KeyR": input.KeyR, + "R": input.KeyR, + "KeyT": input.KeyT, + "T": input.KeyT, + "KeyY": input.KeyY, + "Y": input.KeyY, + "KeyU": input.KeyU, + "U": input.KeyU, + "KeyI": input.KeyI, + "I": input.KeyI, + "KeyO": input.KeyO, + "O": input.KeyO, + "KeyP": input.KeyP, + "P": input.KeyP, + "BracketLeft": input.BracketLeft, + "BracketRight": input.BracketRight, + "CapsLock": input.CapsLock, + "KeyA": input.KeyA, + "A": input.KeyA, + "KeyS": input.KeyS, + "S": input.KeyS, + "KeyD": input.KeyD, + "D": input.KeyD, + "KeyF": input.KeyF, + "F": input.KeyF, + "KeyG": input.KeyG, + "G": input.KeyG, + "KeyH": input.KeyH, + "H": input.KeyH, + "KeyJ": input.KeyJ, + "J": input.KeyJ, + "KeyK": input.KeyK, + "K": input.KeyK, + "KeyL": input.KeyL, + "L": input.KeyL, + "Semicolon": input.Semicolon, + "Quote": input.Quote, + "Enter": input.Enter, + "ShiftLeft": input.ShiftLeft, + "KeyZ": input.KeyZ, + "Z": input.KeyZ, + "KeyX": input.KeyX, + "X": input.KeyX, + "KeyC": input.KeyC, + "C": input.KeyC, + "KeyV": input.KeyV, + "V": input.KeyV, + "KeyB": input.KeyB, + "B": input.KeyB, + "KeyN": input.KeyN, + "N": input.KeyN, + "KeyM": input.KeyM, + "M": input.KeyM, + "Comma": input.Comma, + "Period": input.Period, + "Slash": input.Slash, + "ShiftRight": input.ShiftRight, + "ControlLeft": input.ControlLeft, + "MetaLeft": input.MetaLeft, + "AltLeft": input.AltLeft, + "Space": input.Space, + " ": input.Space, + "AltRight": input.AltRight, + "AltGraph": input.AltGraph, + "MetaRight": input.MetaRight, + "ContextMenu": input.ContextMenu, + "ControlRight": input.ControlRight, + "PrintScreen": input.PrintScreen, + "ScrollLock": input.ScrollLock, + "Pause": input.Pause, + "PageUp": input.PageUp, + "PageDown": input.PageDown, + "Insert": input.Insert, + "Delete": input.Delete, + "Home": input.Home, + "End": input.End, + "ArrowLeft": input.ArrowLeft, + "ArrowUp": input.ArrowUp, + "ArrowRight": input.ArrowRight, + "ArrowDown": input.ArrowDown, + "Left": input.ArrowLeft, + "Up": input.ArrowUp, + "Right": input.ArrowRight, + "Down": input.ArrowDown, + "NumLock": input.NumLock, + "NumpadDivide": input.NumpadDivide, + "NumpadMultiply": input.NumpadMultiply, + "NumpadSubtract": input.NumpadSubtract, + "Numpad7": input.Numpad7, + "Numpad8": input.Numpad8, + "Numpad9": input.Numpad9, + "Numpad4": input.Numpad4, + "Numpad5": input.Numpad5, + "Numpad6": input.Numpad6, + "NumpadAdd": input.NumpadAdd, + "Numpad1": input.Numpad1, + "Numpad2": input.Numpad2, + "Numpad3": input.Numpad3, + "Numpad0": input.Numpad0, + "NumpadDecimal": input.NumpadDecimal, + "NumpadEnter": input.NumpadEnter, + "NumDivide": input.NumpadDivide, + "NumMultiply": input.NumpadMultiply, + "NumSubtract": input.NumpadSubtract, + "Num7": input.Numpad7, + "Num8": input.Numpad8, + "Num9": input.Numpad9, + "Num4": input.Numpad4, + "Num5": input.Numpad5, + "Num6": input.Numpad6, + "NumAdd": input.NumpadAdd, + "Num1": input.Numpad1, + "Num2": input.Numpad2, + "Num3": input.Numpad3, + "Num0": input.Numpad0, + "NumDecimal": input.NumpadDecimal, + "NumEnter": input.NumpadEnter, + "Ctrl": input.ControlLeft, + "Control": input.ControlLeft, + "Cmd": input.MetaLeft, + "Windows": input.MetaLeft, + "Meta": input.MetaLeft, + "Win": input.MetaLeft, + "Option": input.AltLeft, + "Alt": input.AltLeft, + ",": input.Comma, + ".": input.Period, + "/": input.Slash, + ";": input.Semicolon, + "'": input.Quote, + "[": input.BracketLeft, + "]": input.BracketRight, + "\\": input.Backslash, + "-": input.Minus, + "=": input.Equal, + "`": input.Backquote, +} + +func keyFilter(controlKey bool, keys ...string) []string { + var filteredKeys []string + for _, key := range keys { + if (controlKey && isControlKey[key]) || (!controlKey && !isControlKey[key]) { + filteredKeys = append(filteredKeys, key) + } + } + return filteredKeys +} + +func keyAction(page *rod.Page, action string, keys ...string) error { + for _, key := range keys { + if k, ok := KeyMap[key]; ok { + var err error + switch action { + case "press": + err = page.Keyboard.Press(k) + case "release": + err = page.Keyboard.Release(k) + case "type": + err = page.Keyboard.Type(k) + } + if err != nil { + return gojs.Err(err) + } + } + } + return nil +} + +func (pg *Page) KeyPress(keys ...string) error { + return keyAction(pg.page, "press", keys...) +} + +func (pg *Page) KeyRelease(keys ...string) error { + return keyAction(pg.page, "release", keys...) +} + +func (pg *Page) KeyType(keys ...string) error { + return keyAction(pg.page, "type", keys...) +} + +func (pg *Page) Key(keys ...string) error { + controlKeys := keyFilter(true, keys...) + // 按下所有控制键 + err := keyAction(pg.page, "press", controlKeys...) + if err == nil { + // 输入所有非控制键 + err = keyAction(pg.page, "type", keyFilter(false, keys...)...) + } + if err == nil { + // 释放所有控制键 + err = keyAction(pg.page, "release", controlKeys...) + } + return err } func (pg *Page) SetUserAgent(ua string) error { @@ -1092,11 +1350,43 @@ func (el *Element) WaitStable(ms *int) error { } // 元素级 -func (el *Element) Press(key string) error { +func (el *Element) KeyPress(keys ...string) error { if err := el.element.Focus(); err != nil { return gojs.Err(err) } - return gojs.Err(el.element.Page().Keyboard.Press(input.Key(key[0]))) + return keyAction(el.page, "press", keys...) +} + +func (el *Element) KeyRelease(keys ...string) error { + if err := el.element.Focus(); err != nil { + return gojs.Err(err) + } + return keyAction(el.page, "release", keys...) +} + +func (el *Element) KeyType(keys ...string) error { + if err := el.element.Focus(); err != nil { + return gojs.Err(err) + } + return keyAction(el.page, "type", keys...) +} + +func (el *Element) Key(keys ...string) error { + if err := el.element.Focus(); err != nil { + return gojs.Err(err) + } + controlKeys := keyFilter(true, keys...) + // 按下所有控制键 + err := keyAction(el.page, "press", controlKeys...) + if err == nil { + // 输入所有非控制键 + err = keyAction(el.page, "type", keyFilter(false, keys...)...) + } + if err == nil { + // 释放所有控制键 + err = keyAction(el.page, "release", controlKeys...) + } + return err } // IsVisible 判断元素是否可见