nodriver页面加载及JS变量等待失效问题求助
解决方案
一、修复JavaScript变量等待函数
你的wait_for_javascript_data_load失效的核心原因是调用page.evaluate时未指定await_promise=True参数——nodriver不会自动等待页面中返回的Promise完成,只会立即返回Promise对象,导致asyncio.wait_for根本没在等待实际的变量加载逻辑。
另外,轮询方式效率较低,推荐用MutationObserver监听window对象的属性变化,更精准高效:
async def wait_for_javascript_data_load(self, timeout=10): try: await asyncio.wait_for( self.page.evaluate( """ () => new Promise((resolve, reject) => { // 先检查变量是否已存在 const checkVar = () => { const varExists = typeof window.current_variable !== 'undefined' && window.current_variable.hasOwnProperty('id') && window.current_variable.hasOwnProperty('name'); if (varExists) { resolve(); return true; } return false; }; if (checkVar()) return; // 监听window属性变化 const observer = new MutationObserver(() => { if (checkVar()) { observer.disconnect(); resolve(); } }); observer.observe(window, { attributes: true, attributeFilter: ['current_variable'] }); // 超时兜底(避免Observer失效) setTimeout(() => { observer.disconnect(); reject(new Error("JS variable wait timeout")); }, timeout * 1000); }) """, await_promise=True # 必须添加这个参数! ), timeout=timeout ) except asyncio.TimeoutError: self.logger.debug("Timeout while waiting for JS data to load.") except Exception as e: self.logger.debug(f"Error waiting for JS data: {str(e)}")
二、XPath元素的短周期等待实现
针对XPath定位元素、需要短周期检测的场景,不要依赖page.wait_for(仅支持CSS),自己实现轮询检测逻辑,每100ms检查一次,找到元素立即返回,超时则退出:
async def wait_for_xpath_element(self, xpath, timeout=10, interval=0.1): start_time = asyncio.get_event_loop().time() while asyncio.get_event_loop().time() - start_time < timeout: try: element = await self.page.find(xpath, by="xpath") if element: return element except Exception: # 元素未找到时忽略异常,继续轮询 pass await asyncio.sleep(interval) raise TimeoutError(f"XPath element '{xpath}' not found within {timeout}s")
三、优化页面加载等待函数
你之前编写的页面等待函数,需要确保所有page.evaluate调用都添加了await_promise=True,否则同样会出现无法等待Promise完成的问题。以wait_for_network_idle为例,修正后的版本:
import asyncio import random async def wait_for_network_idle(page, timeout=None): if timeout is None: timeout = random.uniform(8, 15) try: await asyncio.wait_for( page.evaluate( """ () => new Promise((resolve) => { let lastNetworkActivity = Date.now(); const observer = new PerformanceObserver((list) => { lastNetworkActivity = Date.now(); }); observer.observe({ entryTypes: ['resource'] }); const checkIdle = () => { if (Date.now() - lastNetworkActivity > 1000) { observer.disconnect(); resolve(); } else { setTimeout(checkIdle, 100); } }; setTimeout(checkIdle, 100); }) """, await_promise=True # 必须添加 ), timeout=timeout ) except asyncio.TimeoutError: raise TimeoutError(f"Network did not become idle within {timeout} s")
关键注意事项
- 所有通过
page.evaluate返回Promise的场景,必须指定await_promise=True,否则nodriver不会等待Promise resolve。 - 对于动态元素或变量,优先使用观察者模式(如
MutationObserver、PerformanceObserver)替代轮询,减少资源消耗。 - 短周期轮询时,合理设置间隔(100ms左右),平衡检测效率和资源占用。
内容的提问来源于stack exchange,提问作者Aca
相关产品推荐
相关产品推荐

