
本文详解 nodriver 中跨页面跳转时的常见错误(如 NoneType 异常和事件循环关闭),强调不能依赖 click() 返回新页面对象,而应通过显式等待导航完成并复用 driver 实例来安全访问后续页面元素。
本文详解 nodriver 中跨页面跳转时的常见错误(如 `nonetype` 异常和事件循环关闭),强调不能依赖 `click()` 返回新页面对象,而应通过显式等待导航完成并复用 `driver` 实例来安全访问后续页面元素。
在使用 nodriver 进行多页网站自动化时,一个典型误区是误以为 element.click() 会返回代表新页面的 Page 对象——实际上,它仅触发点击动作,返回值为 None。若直接对 page2 = await element.click() 的结果调用 .find(),便会触发 AttributeError: 'NoneType' object has no attribute 'find'。更严重的是,未正确管理异步生命周期会导致 RuntimeError: Event loop is closed,这通常源于异常中断后未清理 driver 或未等待异步操作自然结束。
正确的做法是:始终复用原始 driver 实例,并在导航动作后主动等待页面加载就绪。nodriver 提供了 driver.wait_for_navigation() 方法,专门用于阻塞当前协程,直至浏览器完成重定向或页面替换(包括 SPA 路由变化)。此外,下拉菜单交互需先 hover() 再定位子项,而非直接点击父级 <a></a> 标签——否则可能因菜单未展开导致目标元素不可见。
以下是优化后的可靠实现:
import asyncio
import nodriver as uc
import pyautogui
async def b_start_chrome_and_log_in():
print("启动 Chrome 浏览器...")
driver = await uc.start(maximize=True)
# 粗略窗口最大化(建议后续改用 driver.set_window_size())
pyautogui.click(x=1181, y=38)
# 访问首页
await driver.get('https://www.mytpi.com/')
print("首页已加载")
# 定位并悬停“Improve my Game”下拉菜单
improve_menu = await driver.find('/html/body/nav/div/ul/li[4]/a')
await improve_menu.hover()
await asyncio.sleep(0.8) # 短暂等待菜单展开(可替换为显式等待可见性)
# 精确定位并点击下拉项:“Pay per Visit”
pay_option = await driver.find('/html/body/nav/div/ul/li[4]/ul/li[1]/a')
await pay_option.click()
# 关键步骤:等待导航完成,确保 DOM 已更新
await driver.wait_for_navigation()
print("已成功跳转至新页面")
# 在新页面中查找目标元素(无需新建 page 变量)
try:
swing_link = await driver.find(
'/html/body/div[2]/div/div/ul/li[1]/div/a/span',
timeout=10 # 增加超时容错
)
print("✅ 成功定位 'Swing Characteristics' 元素:", swing_link.text)
except uc.TimeoutError:
print("❌ 超时:未找到目标元素,请检查选择器或页面结构是否变更")
# 清理资源(推荐使用 async context manager 更安全)
await driver.close()
# 启动主协程
if __name__ == "__main__":
asyncio.run(b_start_chrome_and_log_in())关键注意事项:
- ✅ 永不假设
click()返回页面对象:所有后续操作均通过driver(非page2)执行; - ✅ 必须调用
wait_for_navigation():这是跨页同步的核心机制,替代不稳定的sleep(); - ✅ 优先使用语义化、健壮的选择器:避免绝对 XPath(如
/html/body/...),改用含id、class或data-*属性的相对路径,例如await driver.find('a[data-nav="swing-characteristics"]'); - ⚠️ 避免混合阻塞式
time.sleep()与异步逻辑:应使用await asyncio.sleep(),但更推荐driver.wait_for(...)配合条件判断; - ? 务必调用
driver.close()或使用async with uc.Chrome() as driver::防止进程残留及事件循环泄漏。
通过以上模式,您可构建出高稳定性、易维护的多页自动化流程,彻底规避 NoneType 和事件循环关闭等典型陷阱。

















