Golang使用Chromedp元素不存在触发deadline exceeded,延展超时仍报错如何解决?
问题根因
- 上下文派生规则错误:Go语言中
context.WithTimeout派生的子上下文的deadline不会超过父上下文的deadline。你第一次给ctx附加了10秒超时后,后续用这个ctx派生的30秒超时上下文实际生效的还是10秒的deadline,所以你拉长超时时长完全不生效。而你缩短前序超时时长反而正常,本质是前序操作更早完成,给后续元素匹配留了更多有效时间,属于巧合。 - 元素选择器参数错误:第二段
chromedp.Click传入的匹配规则是chromedp.ByID,但你写的选择器是完整CSS路径,不是元素ID,导致框架一直找不到对应元素,最终触发超时。 - 代码小错误:最后报错打印的是第一个操作的
err,不是第二个操作的err2,排查问题时会输出错误的故障原因。
修复方案
首先单独保存无超时的基础chromedp上下文,第二阶段的超时从基础上下文派生,确保30秒超时生效;同时修正选择器匹配规则、错误输出逻辑即可,修复后代码如下:
func check(page string) { opts := append(chromedp.DefaultExecAllocatorOptions[:], chromedp.UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.106 Safari/537.36"), chromedp.Flag("enable-automation", false), chromedp.Flag("headless", false), ) allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) defer cancel() // 单独保存无超时的基础chromedp上下文,后续派生新超时都用这个变量 baseCtx, cancel := chromedp.NewContext(allocCtx) defer cancel() // 第一个阶段:处理弹窗,10秒超时 popupCtx, cancel := context.WithTimeout(baseCtx, 10*time.Second) defer cancel() err := chromedp.Run(popupCtx, chromedp.Navigate(page), chromedp.WaitReady("body", chromedp.ByQuery), chromedp.Click(`#ackBtn`, chromedp.ByID), ) if err != nil { fmt.Println("Didn't find Ack") } // 第二个阶段:点击按钮,从baseCtx派生30秒超时,不受前序10秒限制 actionCtx, cancel := context.WithTimeout(baseCtx, 30*time.Second) defer cancel() err2 := chromedp.Run(actionCtx, // 匹配规则改成ByQuery,对应CSS选择器逻辑 chromedp.Click(`#options > div:nth-child(1) > div.c-card__column2 > a.c-card__btn.btn-new.btn-color-blue.btn-size-xxlarge.btn-width-auto.btn-max-width`, chromedp.ByQuery), ) if err2 != nil { // 打印正确的错误对象 fmt.Println("exited on error", err2) } }
如果要优化弹窗处理逻辑,避免无弹窗时硬等10秒,可以用如下逻辑快速判断弹窗是否存在:
// 替换原来popupCtx对应的chromedp.Run内容即可 err := chromedp.Run(popupCtx, chromedp.Navigate(page), chromedp.WaitReady("body", chromedp.ByQuery), chromedp.ActionFunc(func(ctx context.Context) error { var ackExist bool if err := chromedp.Evaluate(`!!document.getElementById("ackBtn")`, &ackExist).Do(ctx); err != nil { return err } if ackExist { return chromedp.Click(`#ackBtn`, chromedp.ByID).Do(ctx) } return nil }), )
内容的提问来源于stack exchange,提问作者vinniyo
相关产品推荐
相关产品推荐

