
本文详解 nodriver 中页面跳转后无法继续操作的根本原因及解决方案,重点解决 click() 不返回新页面、page2 为 None 导致的 AttributeError 和 Event Loop 关闭异常,提供稳定可靠的多页自动化流程。
本文详解 nodriver 中页面跳转后无法继续操作的根本原因及解决方案,重点解决 `click()` 不返回新页面、`page2` 为 `none` 导致的 attributeerror 和 event loop 关闭异常,提供稳定可靠的多页自动化流程。
在使用 nodriver 进行网页自动化时,一个常见误区是认为元素的 .click() 方法会同步返回新页面对象(如 page2 = await element.click())。事实并非如此:click() 仅触发点击行为,不阻塞执行,也不返回页面实例。若直接对返回值(实际为 None)调用 .find(),将立即抛出 AttributeError: 'NoneType' object has no attribute 'find';后续未正确关闭驱动或未等待导航完成,还会引发 RuntimeError: Event loop is closed 等清理异常。
✅ 正确做法:以 driver 为唯一上下文,显式等待导航完成
nodriver 的设计哲学是“单驱动多页面”,所有页面操作均通过 driver 实例统一管理。新页面加载后,无需获取新 page 对象,而应:
- 先触发交互(如 hover + click)
-
再调用
await driver.wait_for_navigation()显式等待页面跳转完成 - 最后用
await driver.find(...)定位新页面元素
以下为修复后的完整示例代码(已移除不必要依赖,增强健壮性):
import asyncio
import nodriver as uc
import pyautogui
async def b_start_chrome_and_log_in():
print("✅ 启动 Chrome 浏览器...")
driver = await uc.start(maximize=True)
# (可选)强制最大化窗口(建议优先使用 maximize=True)
# pyautogui.click(x=1181, y=38)
print("? 访问目标网站...")
await driver.get('https://www.mytpi.com/')
# 步骤1:定位并悬停下拉菜单项
improve_menu = await driver.find('nav ul li:nth-child(4) > a', timeout=10)
await improve_menu.hover()
print("➡️ 已悬停 'Improve my Game' 下拉菜单")
# 步骤2:精准定位并点击子菜单项(避免硬编码 XPath)
pay_option = await driver.find('nav ul li:nth-child(4) ul li:first-child a', timeout=10)
await pay_option.click()
print("?️ 已点击 'Pay per Visit'")
# ✅ 关键步骤:等待导航完成(替代 sleep,更可靠)
await driver.wait_for_navigation()
print("? 页面跳转已完成")
# 步骤3:在新页面中查找目标元素(推荐使用 CSS 选择器,更稳定)
try:
swing_link = await driver.find(
'div#main-content ul li:first-child div a span',
timeout=15
)
print("? 成功定位 'Swing Characteristics' 元素:", swing_link.text)
except uc.TimeoutError:
print("❌ 超时:未能在新页面中找到目标元素,请检查选择器或网络状态")
# ✅ 必须显式关闭驱动,防止事件循环异常
await driver.close()
if __name__ == "__main__":
asyncio.run(b_start_chrome_and_log_in())⚠️ 注意事项与最佳实践
-
避免硬编码 XPath:XPath 极易因页面结构微调而失效。优先使用语义化 CSS 选择器(如
nav ul li:nth-child(4) a),配合timeout参数提升容错性; -
禁用
sleep()替代等待:time.sleep()是阻塞式且不可靠的;务必使用await driver.wait_for_navigation()或await driver.find(..., timeout=N)实现异步等待; -
始终显式关闭驱动:
await driver.close()不仅释放资源,还能确保事件循环正常退出,避免Event loop is closed异常; -
错误处理不可省略:对关键
find()操作添加try/except uc.TimeoutError,便于调试和降级处理; -
不要保存
click()返回值:element.click()返回None,任何赋值(如page2 = await elem.click())都无意义,应直接操作driver。
通过以上重构,您将获得一个可维护、可扩展、高鲁棒性的多页自动化脚本——不再依赖“页面引用传递”,而是聚焦于驱动状态管理与导航生命周期控制,这才是 nodriver 的正确使用范式。

















