rod_utils.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. package common
  2. import (
  3. "errors"
  4. "git.listensoft.net/tool/jspkit/common/lxrod"
  5. "git.listensoft.net/tool/jspkit/logger"
  6. "git.listensoft.net/tool/jspkit/taxerr"
  7. "github.com/go-rod/rod"
  8. "github.com/go-rod/rod/lib/input"
  9. "github.com/go-rod/rod/lib/proto"
  10. "github.com/go-rod/rod/lib/utils"
  11. "go.uber.org/zap"
  12. "strings"
  13. "time"
  14. )
  15. func MustHas(page *rod.Page, xpath string) bool {
  16. x, _, _ := HasX(page, xpath)
  17. return x
  18. }
  19. func MustHasX(page *rod.Page, xpath string) bool {
  20. x, _, _ := HasX(page, xpath)
  21. return x
  22. }
  23. func MustHasXV(page *rod.Page, xpath string) bool {
  24. for _, element := range MustElementsX(page, xpath) {
  25. visible, _ := element.Visible()
  26. if visible {
  27. return true
  28. }
  29. }
  30. return false
  31. }
  32. func MustElementX(page *rod.Page, xpath string) *rod.Element {
  33. for i := 0; i < 5; i++ {
  34. x, _ := ElementX(page, xpath)
  35. if x != nil {
  36. return x
  37. }
  38. utils.Sleep(1)
  39. }
  40. return nil
  41. }
  42. func MustHasXV1(page *rod.Page, xpath string) bool {
  43. for _, element := range MustElementsX1(page, xpath) {
  44. visible, _ := element.Visible()
  45. if visible {
  46. return true
  47. }
  48. }
  49. return false
  50. }
  51. func HasX(page *rod.Page, xpath string) (bool, *rod.Element, error) {
  52. var x *rod.Element
  53. var has bool
  54. var err error
  55. err = rod.Try(func() {
  56. has, x, err = page.HasX(xpath)
  57. if has {
  58. return
  59. }
  60. if err != nil {
  61. panic(err)
  62. }
  63. if has {
  64. return
  65. }
  66. iframe := GetAllIframe(page)
  67. for _, pg := range iframe {
  68. has, x, err = pg.HasX(xpath)
  69. if has {
  70. return
  71. }
  72. if err != nil {
  73. panic(err)
  74. }
  75. if has {
  76. return
  77. }
  78. }
  79. })
  80. if x != nil {
  81. x = x.Timeout(time.Second * 15)
  82. }
  83. return has, x, err
  84. }
  85. func ElementX(page *rod.Page, xpath string) (*rod.Element, error) {
  86. _, element, err := HasX(page, xpath)
  87. if err != nil {
  88. err = taxerr.NewWebStuckTitle(false)
  89. }
  90. return element, err
  91. }
  92. func GetAllIframe(page *rod.Page) []*rod.Page {
  93. fs := make([]*rod.Page, 0)
  94. if page.MustHasX(`//iframe`) {
  95. for _, element := range page.MustElementsX(`//iframe`) {
  96. fs = append(fs, element.MustFrame())
  97. fs = append(fs, GetAllIframe(element.MustFrame())...)
  98. }
  99. }
  100. return fs
  101. }
  102. func MustElementXV(page *rod.Page, xpath string) *rod.Element {
  103. for i := 0; i < 5; i++ {
  104. x, _ := ElementsX(page, xpath)
  105. for _, element := range x {
  106. visible, _ := element.Visible()
  107. if visible {
  108. return element
  109. }
  110. }
  111. utils.Sleep(1)
  112. }
  113. return nil
  114. }
  115. func MustElementsX(page *rod.Page, xpath string) (x []*rod.Element) {
  116. for i := 0; i < 5; i++ {
  117. x, _ = ElementsX(page, xpath)
  118. if len(x) != 0 {
  119. break
  120. }
  121. utils.Sleep(1)
  122. }
  123. return x
  124. }
  125. func MustElementsX1(page *rod.Page, xpath string) (x []*rod.Element) {
  126. x, _ = ElementsX(page, xpath)
  127. return x
  128. }
  129. func ElementsX(page *rod.Page, xpath string) ([]*rod.Element, error) {
  130. var arr []*rod.Element
  131. var err error
  132. err = rod.Try(func() {
  133. r, _ := page.Timeout(ClickTimeOut).ElementsX(xpath)
  134. if err != nil {
  135. panic(err)
  136. }
  137. arr = append(arr, r...)
  138. for _, pg := range GetAllIframe(page) {
  139. pg.MustWaitLoad()
  140. r, err = pg.Timeout(ClickTimeOut).ElementsX(xpath)
  141. if err != nil {
  142. panic(err)
  143. }
  144. arr = append(arr, r...)
  145. }
  146. })
  147. return arr, err
  148. }
  149. func WaitElementX(page *rod.Page, xpath string, interval float64) *rod.Element {
  150. for i := 0; i < 60; i++ {
  151. if has, el, _ := HasX(page, xpath); has {
  152. return el
  153. }
  154. time.Sleep(time.Duration(float64(time.Second) * interval))
  155. }
  156. return nil
  157. }
  158. func WaitNavigation(page *rod.Page, url string) {
  159. navigation := page.Timeout(time.Minute).MustWaitNavigation()
  160. page.Timeout(time.Minute).MustNavigate(url)
  161. logger.Info("调试埋点11-1")
  162. utils.Sleep(1)
  163. WaitTimeOut(navigation, time.Minute)
  164. logger.Info("埋点11-2")
  165. }
  166. func WaitTimeOut(f func(), timeout time.Duration) {
  167. resultChan := make(chan struct{}, 1)
  168. go func() {
  169. f()
  170. resultChan <- struct{}{}
  171. }()
  172. select {
  173. case <-resultChan:
  174. return
  175. case <-time.After(timeout):
  176. panic(taxerr.NewWebStuckTitle(ComInfo.Cscsts))
  177. }
  178. }
  179. func Link(p *rod.Page) {
  180. go func() {
  181. //for {
  182. //utils.Sleep(1)
  183. if p == nil {
  184. return
  185. }
  186. rod.Try(func() {
  187. lxrod.DialogWatch(nil, p)
  188. go p.Browser().EachEvent(func(e *proto.TargetTargetCreated) {
  189. defer func() {
  190. if r := recover(); r != nil {
  191. logger.Error("EachPageCreatedEvent recover:", zap.Any("r", r))
  192. }
  193. }()
  194. if e.TargetInfo.Type != proto.TargetTargetInfoTypePage {
  195. return
  196. }
  197. page := p.Browser().MustPageFromTargetID(e.TargetInfo.TargetID)
  198. lxrod.DialogWatch(nil, page)
  199. })()
  200. // has, _, _ := p.Has("dialog")
  201. // if has {
  202. // _, _ = p.Eval(`
  203. //window.alert = function() {};
  204. //window.confirm = function() { return true; };
  205. //window.prompt = function() {};`)
  206. // }
  207. })
  208. //}
  209. }()
  210. }
  211. // 有就点 没有就不点击
  212. func MustClick(page *rod.Page, xpath string) {
  213. x := MustElementsX(page, xpath)
  214. for _, element := range x {
  215. visible, _ := element.Visible()
  216. if visible {
  217. element.MustClick()
  218. break
  219. }
  220. }
  221. }
  222. func MustText(page *rod.Page, xpath string) string {
  223. //去掉左右空格
  224. return strings.TrimSpace(MustElementX(page, xpath).MustText())
  225. }
  226. // 输入文字
  227. func Input(p *rod.Page, selector, inputVal string) {
  228. err := rod.Try(func() {
  229. if inputVal == "" || inputVal == "*" {
  230. inputVal = "0"
  231. }
  232. e := rod.Try(func() {
  233. p.Timeout(time.Second).MustSearch(selector).MustSelectAllText().MustFrame().Keyboard.MustType(input.Backspace)
  234. })
  235. if e != nil {
  236. if w := errors.Unwrap(e); w != nil {
  237. logger.Info(w.Error() + "-" + selector + "-" + inputVal)
  238. } else {
  239. logger.Info(e.Error() + "-" + selector + "-" + inputVal)
  240. }
  241. }
  242. e = rod.Try(func() {
  243. p.Timeout(time.Second).MustSearch(selector).MustSelectAllText().MustInput(inputVal)
  244. })
  245. if e != nil {
  246. if w := errors.Unwrap(e); w != nil {
  247. logger.Info(strings.Split(w.Error(), "\t")[0] + "-" + selector + "-" + inputVal)
  248. } else {
  249. logger.Info(strings.Split(e.Error(), "\t")[0] + "-" + selector + "-" + inputVal)
  250. }
  251. }
  252. })
  253. if err != nil {
  254. logger.Info(err.Error() + "-" + selector + "-" + inputVal)
  255. }
  256. }
  257. // 输入文字
  258. func MustInputX(p *rod.Page, xPath, inputVal string) {
  259. e := MustElementX(p, xPath)
  260. _ = e.ScrollIntoView()
  261. e.MustClick().MustSelectAllText().MustInput(inputVal)
  262. }
  263. // 拦截请求
  264. func PageHijackReq(b *rod.Page, pattern string, ch chan []byte) *rod.HijackRouter {
  265. router := b.HijackRequests()
  266. router.MustAdd(pattern, func(ctx *rod.Hijack) {
  267. rod.Try(func() {
  268. ctx.MustLoadResponse()
  269. ch <- []byte(ctx.Response.Body())
  270. utils.Sleep(5)
  271. select {
  272. case <-ch:
  273. break
  274. }
  275. })
  276. })
  277. go router.Run()
  278. return router
  279. }
  280. // 获取拦截请求的值
  281. func GetHijackResp(ch chan []byte, t float64) ([]byte, error) {
  282. d := time.Duration(t * float64(time.Second))
  283. select {
  284. case val := <-ch:
  285. return val, nil
  286. case <-time.After(d):
  287. return nil, errors.New("请求网页超时,请稍后重试!")
  288. }
  289. }
  290. // WaitForPageLoad 等待页面完全加载,selector 为等待某个元素加载的选择器,可选
  291. func WaitForPageLoad(page *rod.Page, timeoutDuration time.Duration, selector ...string) {
  292. start := time.Now()
  293. // 1. 等待页面 readyState 为 "complete",加上超时控制
  294. for {
  295. readyState := page.MustEval(`() => document.readyState`).String()
  296. if readyState == "complete" {
  297. break // 页面加载完成,退出循环
  298. }
  299. // 检查是否超时
  300. if time.Since(start) > timeoutDuration {
  301. break
  302. }
  303. time.Sleep(500 * time.Millisecond) // 每次检查之间等待一段时间
  304. }
  305. // 2. 等待页面元素稳定
  306. page.MustWaitStable()
  307. // 3. 如果传入了 selector,等待特定元素加载完成
  308. if len(selector) > 0 {
  309. rod.Try(func() {
  310. page.Timeout(timeoutDuration).MustElement(selector[0]) // 使用传入的选择器等待元素
  311. })
  312. }
  313. // 4. 等待所有网络请求结束
  314. rod.Try(func() {
  315. page.Timeout(timeoutDuration).MustWaitIdle()
  316. })
  317. utils.Sleep(3)
  318. }