Golang中使用chromedp设置超时的方法

Golang中使用chromedp设置超时的方法 我正在使用 chromedp 在新标签页中打开链接时启动一个新的浏览器上下文。在调试过程中,由于是远程运行,我可以直观地看到屏幕上显示的内容。

这是一个独立的问题,尽管链接存在且与我使用的 xpath 相同,但我的应用程序会无限期挂起。因此,我尝试仅用 context.WithTimeout(...) 包装现有的上下文,但遇到了问题。

目前,我使用 ActionFunc 包装 WaitVisible 函数,并在其中处理超时。最后,我使用一个 select 块来获取新打开标签页的目标 ID 或超时信号,以先发生者为准。

这是我目前的代码:

func (i *Instance) tryLaunch() error {
	log.Info().Msgf("launching instance: %d → %d @ [%s]", i, i.Index, action.Location(i.session.ctx))
	targetElementXpath := fmt.Sprintf("//*[@id="home-screen"]/div[2]/section[5]/div[5]/div/ul/li[%d]/a[1]", i.Index)
	targetIDChannel := chromedp.WaitNewTarget(i.session.ctx, matchTabWithNonEmptyURL)

	waitTime := 5 * time.Second

	err := chromedp.Run(i.session.ctx, chromedp.ActionFunc(func(ctx context.Context) error {
		timeLimitedCtx, timeLimitedCancel := context.WithTimeout(i.session.ctx, waitTime)
		defer timeLimitedCancel()

		log.Info().Msgf("waiting for target element to be visible: %s", targetElementXpath)
		return chromedp.WaitVisible(targetElementXpath).Do(timeLimitedCtx)
	}),
		chromedp.Click(targetElementXpath),
	)

	if err != nil {
		return err
	}

	log.Info().Msg("initializing new context from new tab")

	select {
	case targetId := <-targetIDChannel:
		newInstance, newCancelFunc := chromedp.NewContext(i.session.ctx, chromedp.WithTargetID(targetId))
		i.ctx = newInstance
		i.cancel = newCancelFunc
		return nil
	case <-time.After(waitTime):
		log.Warn().Msg("timed out waiting for targetID")
		return errors.New("operation timed out")
	}
}

在这段代码之前,我尝试过不将 WaitVisible 包装在 ActionFunc 中,而是直接传入 timeLimitedContext。那样做似乎完全没有效果。在目前的状态下,我最终遇到了这个错误:

panic: interface conversion: interface is nil, not cdp.Executor

这个错误发生在这一行:

err := chromedp.Run(i.session.ctx, chromedp.ActionFunc(func(ctx context.Context) error {

更多关于Golang中使用chromedp设置超时的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

你好,

代码中的 panic: interface conversion: interface is nil, not cdp.Executor 错误表明,传递给内部 chromedp.ActionFuncctx 不再包含有效的 cdp.Executor。这通常发生在你未正确管理或重新包装上下文时,例如,在一个已被剥离了浏览器执行器/会话绑定的父上下文上调用 context.WithTimeout

此致 猫语翻译应用

更多关于Golang中使用chromedp设置超时的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在 chromedp 中设置超时需要正确处理上下文传递。你的代码问题在于 timeLimitedCtx 没有正确继承 chromedp 的执行器。以下是修正后的实现:

func (i *Instance) tryLaunch() error {
    log.Info().Msgf("launching instance: %d → %d @ [%s]", i, i.Index, action.Location(i.session.ctx))
    
    targetElementXpath := fmt.Sprintf("//*[@id='home-screen']/div[2]/section[5]/div[5]/div/ul/li[%d]/a[1]", i.Index)
    targetIDChannel := chromedp.WaitNewTarget(i.session.ctx, matchTabWithNonEmptyURL)
    
    waitTime := 5 * time.Second
    timeoutCtx, cancel := context.WithTimeout(i.session.ctx, waitTime)
    defer cancel()

    // 创建带超时的 ActionFunc
    waitAction := chromedp.ActionFunc(func(ctx context.Context) error {
        log.Info().Msgf("waiting for target element to be visible: %s", targetElementXpath)
        return chromedp.WaitVisible(targetElementXpath).Do(ctx)
    })

    // 使用超时上下文运行
    err := chromedp.Run(timeoutCtx,
        waitAction,
        chromedp.Click(targetElementXpath),
    )

    if err != nil {
        if errors.Is(err, context.DeadlineExceeded) {
            log.Warn().Msg("wait visible timed out")
            return errors.New("operation timed out")
        }
        return err
    }

    log.Info().Msg("initializing new context from new tab")

    select {
    case targetId := <-targetIDChannel:
        newInstance, newCancelFunc := chromedp.NewContext(i.session.ctx, chromedp.WithTargetID(targetId))
        i.ctx = newInstance
        i.cancel = newCancelFunc
        return nil
    case <-time.After(waitTime):
        log.Warn().Msg("timed out waiting for targetID")
        return errors.New("operation timed out")
    }
}

或者使用更简洁的 chromedp.WithTimeout 选项:

func (i *Instance) tryLaunch() error {
    log.Info().Msgf("launching instance: %d → %d @ [%s]", i, i.Index, action.Location(i.session.ctx))
    
    targetElementXpath := fmt.Sprintf("//*[@id='home-screen']/div[2]/section[5]/div[5]/div/ul/li[%d]/a[1]", i.Index)
    targetIDChannel := chromedp.WaitNewTarget(i.session.ctx, matchTabWithNonEmptyURL)
    
    waitTime := 5 * time.Second

    // 创建带超时的上下文
    timeoutCtx, cancel := context.WithTimeout(i.session.ctx, waitTime)
    defer cancel()

    err := chromedp.Run(timeoutCtx,
        chromedp.WaitVisible(targetElementXpath),
        chromedp.Click(targetElementXpath),
    )

    if err != nil {
        return err
    }

    log.Info().Msg("initializing new context from new tab")

    select {
    case targetId := <-targetIDChannel:
        newInstance, newCancelFunc := chromedp.NewContext(i.session.ctx, chromedp.WithTargetID(targetId))
        i.ctx = newInstance
        i.cancel = newCancelFunc
        return nil
    case <-time.After(waitTime):
        log.Warn().Msg("timed out waiting for targetID")
        return errors.New("operation timed out")
    }
}

关键点:

  1. 直接对 chromedp.Run 使用带超时的上下文
  2. 避免在 ActionFunc 内部创建新的上下文,这会丢失执行器信息
  3. 检查 context.DeadlineExceeded 错误来识别超时

你的原始代码中 panic 是因为 timeLimitedCtx 没有继承 chromedp 的执行器接口,导致 Do() 方法调用时出现类型断言失败。

回到顶部