You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 05:14:52