使用nodriver实现多页面跳转时获取新页面元素报错:AttributeError及Event Loop已关闭问题
nodriver实现多页面跳转时获取新页面元素报错:AttributeError及Event Loop已关闭问题
我来帮你分析和解决这两个问题,看起来都是对nodriver的异步页面跳转逻辑理解不到位导致的:
首先看AttributeError: 'NoneType' object has no attribute 'find'的原因
你这里的page2 = await improve.click()是错误的——nodriver里元素的click()方法只是执行点击动作,不会返回新页面的实例,所以page2其实是None,这就导致后面调用page2.find()直接报错。
要获取跳转后的新页面,应该从driver的pages列表里取,因为新打开的页面会自动添加到这个列表中。
然后是RuntimeError: Event loop is closed的问题
这个问题主要是两个原因:
- 你混用了同步的
time.sleep()和异步代码,同步sleep会阻塞asyncio的事件循环,导致nodriver无法正常处理页面事件,后续资源清理时出问题; - 程序报错退出时,driver没有被正确关闭,导致子进程的资源没有被释放,当事件循环已经关闭后,子进程的transport还尝试操作,就会抛出这个错误。
修正后的完整代码
import asyncio import nodriver as uc import pyautogui async def b_start_chrome_and_log_in(): driver = None try: print("start chrome") # 启动Chrome实例 driver = await uc.start(maximize=True) # 窗口最大化(如果nodriver的maximize=True生效,这步可以去掉) pyautogui.click(x=1181, y=38) # 访问目标网站 page = await driver.get('https://www.mytpi.com/') # 用asyncio.sleep替代time.sleep,避免阻塞事件循环 await asyncio.sleep(2) # 定位下拉菜单元素 improve = await page.find('/html/body/nav/div/ul/li[4]/a') await asyncio.sleep(2) # 记录当前页面数量,用于判断新页面是否加载 original_page_count = len(driver.pages) # 执行点击动作 await improve.click() # 等待新页面出现 while len(driver.pages) == original_page_count: await asyncio.sleep(0.3) # 获取最新打开的页面(列表最后一个元素) page2 = driver.pages[-1] await asyncio.sleep(2) # 定位目标元素 swing = await page2.find('/html/body/div[2]/div/div/ul/li[1]/div/a/span') print("成功找到目标元素!") finally: # 确保无论是否报错,driver都能被正确关闭 if driver is not None: await driver.close() asyncio.run(b_start_chrome_and_log_in())
额外的几个建议
- 尽量避免使用绝对xpath,页面结构稍微变动就会定位失败,优先用元素的
id、class或者文本内容来定位,比如await page.find('.improve-menu', by='css')或者await page.find('Improve my Game', by='text'); - 如果nodriver版本支持,可以用
await driver.wait_for_new_page()来替代手动轮询页面数量,这个方法更优雅; - 不要依赖固定的sleep时长,可以用
await page.wait_for_selector()来等待元素加载,比固定sleep更可靠,比如await page2.wait_for_selector('/html/body/div[2]/div/div/ul/li[1]/div/a/span')之后再去find元素。
备注:内容来源于stack exchange,提问作者steve
相关产品推荐
相关产品推荐

