package common import ( "errors" "git.listensoft.net/tool/jspkit/common/lxrod" "git.listensoft.net/tool/jspkit/logger" "git.listensoft.net/tool/jspkit/taxerr" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/input" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "go.uber.org/zap" "strings" "time" ) func MustHas(page *rod.Page, xpath string) bool { x, _, _ := HasX(page, xpath) return x } func MustHasX(page *rod.Page, xpath string) bool { x, _, _ := HasX(page, xpath) return x } func MustHasXV(page *rod.Page, xpath string) bool { for _, element := range MustElementsX(page, xpath) { visible, _ := element.Visible() if visible { return true } } return false } func MustElementX(page *rod.Page, xpath string) *rod.Element { for i := 0; i < 5; i++ { x, _ := ElementX(page, xpath) if x != nil { return x } utils.Sleep(1) } return nil } func MustHasXV1(page *rod.Page, xpath string) bool { for _, element := range MustElementsX1(page, xpath) { visible, _ := element.Visible() if visible { return true } } return false } func HasX(page *rod.Page, xpath string) (bool, *rod.Element, error) { var x *rod.Element var has bool var err error err = rod.Try(func() { has, x, err = page.HasX(xpath) if has { return } if err != nil { panic(err) } if has { return } iframe := GetAllIframe(page) for _, pg := range iframe { has, x, err = pg.HasX(xpath) if has { return } if err != nil { panic(err) } if has { return } } }) if x != nil { x = x.Timeout(time.Second * 15) } return has, x, err } func ElementX(page *rod.Page, xpath string) (*rod.Element, error) { _, element, err := HasX(page, xpath) if err != nil { err = taxerr.NewWebStuckTitle(false) } return element, err } func GetAllIframe(page *rod.Page) []*rod.Page { fs := make([]*rod.Page, 0) if page.MustHasX(`//iframe`) { for _, element := range page.MustElementsX(`//iframe`) { fs = append(fs, element.MustFrame()) fs = append(fs, GetAllIframe(element.MustFrame())...) } } return fs } func MustElementXV(page *rod.Page, xpath string) *rod.Element { for i := 0; i < 5; i++ { x, _ := ElementsX(page, xpath) for _, element := range x { visible, _ := element.Visible() if visible { return element } } utils.Sleep(1) } return nil } func MustElementsX(page *rod.Page, xpath string) (x []*rod.Element) { for i := 0; i < 5; i++ { x, _ = ElementsX(page, xpath) if len(x) != 0 { break } utils.Sleep(1) } return x } func MustElementsX1(page *rod.Page, xpath string) (x []*rod.Element) { x, _ = ElementsX(page, xpath) return x } func ElementsX(page *rod.Page, xpath string) ([]*rod.Element, error) { var arr []*rod.Element var err error err = rod.Try(func() { r, _ := page.Timeout(ClickTimeOut).ElementsX(xpath) if err != nil { panic(err) } arr = append(arr, r...) for _, pg := range GetAllIframe(page) { pg.MustWaitLoad() r, err = pg.Timeout(ClickTimeOut).ElementsX(xpath) if err != nil { panic(err) } arr = append(arr, r...) } }) return arr, err } func WaitElementX(page *rod.Page, xpath string, interval float64) *rod.Element { for i := 0; i < 60; i++ { if has, el, _ := HasX(page, xpath); has { return el } time.Sleep(time.Duration(float64(time.Second) * interval)) } return nil } func WaitNavigation(page *rod.Page, url string) { navigation := page.Timeout(time.Minute).MustWaitNavigation() page.Timeout(time.Minute).MustNavigate(url) logger.Info("调试埋点11-1") utils.Sleep(1) WaitTimeOut(navigation, time.Minute) logger.Info("埋点11-2") } func WaitTimeOut(f func(), timeout time.Duration) { resultChan := make(chan struct{}, 1) go func() { f() resultChan <- struct{}{} }() select { case <-resultChan: return case <-time.After(timeout): panic(taxerr.NewWebStuckTitle(ComInfo.Cscsts)) } } func Link(p *rod.Page) { go func() { //for { //utils.Sleep(1) if p == nil { return } rod.Try(func() { lxrod.DialogWatch(nil, p) go p.Browser().EachEvent(func(e *proto.TargetTargetCreated) { defer func() { if r := recover(); r != nil { logger.Error("EachPageCreatedEvent recover:", zap.Any("r", r)) } }() if e.TargetInfo.Type != proto.TargetTargetInfoTypePage { return } page := p.Browser().MustPageFromTargetID(e.TargetInfo.TargetID) lxrod.DialogWatch(nil, page) })() // has, _, _ := p.Has("dialog") // if has { // _, _ = p.Eval(` //window.alert = function() {}; //window.confirm = function() { return true; }; //window.prompt = function() {};`) // } }) //} }() } // 有就点 没有就不点击 func MustClick(page *rod.Page, xpath string) { x := MustElementsX(page, xpath) for _, element := range x { visible, _ := element.Visible() if visible { element.MustClick() break } } } func MustText(page *rod.Page, xpath string) string { //去掉左右空格 return strings.TrimSpace(MustElementX(page, xpath).MustText()) } // 输入文字 func Input(p *rod.Page, selector, inputVal string) { err := rod.Try(func() { if inputVal == "" || inputVal == "*" { inputVal = "0" } e := rod.Try(func() { p.Timeout(time.Second).MustSearch(selector).MustSelectAllText().MustFrame().Keyboard.MustType(input.Backspace) }) if e != nil { if w := errors.Unwrap(e); w != nil { logger.Info(w.Error() + "-" + selector + "-" + inputVal) } else { logger.Info(e.Error() + "-" + selector + "-" + inputVal) } } e = rod.Try(func() { p.Timeout(time.Second).MustSearch(selector).MustSelectAllText().MustInput(inputVal) }) if e != nil { if w := errors.Unwrap(e); w != nil { logger.Info(strings.Split(w.Error(), "\t")[0] + "-" + selector + "-" + inputVal) } else { logger.Info(strings.Split(e.Error(), "\t")[0] + "-" + selector + "-" + inputVal) } } }) if err != nil { logger.Info(err.Error() + "-" + selector + "-" + inputVal) } } // 输入文字 func MustInputX(p *rod.Page, xPath, inputVal string) { e := MustElementX(p, xPath) _ = e.ScrollIntoView() e.MustClick().MustSelectAllText().MustInput(inputVal) } // 拦截请求 func PageHijackReq(b *rod.Page, pattern string, ch chan []byte) *rod.HijackRouter { router := b.HijackRequests() router.MustAdd(pattern, func(ctx *rod.Hijack) { rod.Try(func() { ctx.MustLoadResponse() ch <- []byte(ctx.Response.Body()) utils.Sleep(5) select { case <-ch: break } }) }) go router.Run() return router } // 获取拦截请求的值 func GetHijackResp(ch chan []byte, t float64) ([]byte, error) { d := time.Duration(t * float64(time.Second)) select { case val := <-ch: return val, nil case <-time.After(d): return nil, errors.New("请求网页超时,请稍后重试!") } } // WaitForPageLoad 等待页面完全加载,selector 为等待某个元素加载的选择器,可选 func WaitForPageLoad(page *rod.Page, timeoutDuration time.Duration, selector ...string) { start := time.Now() // 1. 等待页面 readyState 为 "complete",加上超时控制 for { readyState := page.MustEval(`() => document.readyState`).String() if readyState == "complete" { break // 页面加载完成,退出循环 } // 检查是否超时 if time.Since(start) > timeoutDuration { break } time.Sleep(500 * time.Millisecond) // 每次检查之间等待一段时间 } // 2. 等待页面元素稳定 page.MustWaitStable() // 3. 如果传入了 selector,等待特定元素加载完成 if len(selector) > 0 { rod.Try(func() { page.Timeout(timeoutDuration).MustElement(selector[0]) // 使用传入的选择器等待元素 }) } // 4. 等待所有网络请求结束 rod.Try(func() { page.Timeout(timeoutDuration).MustWaitIdle() }) utils.Sleep(3) }