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

Apify如何阻止enqueueLinks函数打开浏览器新页面引发节点报错

问题根因
  • enqueueLinks参数使用错误:baseUrl参数的作用是补全页面上的相对路径链接为绝对路径,不是用来做链接过滤的,你当前写的正则形式过滤规则放在baseUrl里完全不生效,会导致enqueueLinks匹配逻辑异常,误触发链接跳转打开新标签页。正确的链接过滤应该使用pseudoUrls参数。
  • DOM元素句柄失效:你在进入页面时就一次性通过$x获取了所有分页按钮的ElementHandle,但只要点击按钮触发页面跳转/刷新,之前获取的所有元素引用都会和已销毁的旧DOM树解绑,后续循环再点击这些失效句柄就会触发「节点未找到」错误。
  • 点击分页后缺少有效页面等待逻辑:点击按钮后只等200ms就执行链接提取,此时新页面的DOM还没加载完成,不仅可能拿不到目标链接,还会加剧元素失效问题。
修复方案
  1. 修正enqueueLinks的参数,把链接过滤规则从baseUrl移到pseudoUrls,baseUrl只传站点根路径用来补全相对链接即可。
  2. 放弃提前获取所有分页按钮的写法,每次页面加载完成后,重新查找当前页的下一页按钮,点击后等待页面导航完成,再执行链接提取,直到找不到下一页按钮为止。
  3. 点击按钮后不要用固定时长等待,改用waitForNavigation等待页面导航完成,兼顾爬取效率和稳定性。
修复后代码
exports.handleList = async ({ request, page, log }, requestQueue) => {
    // 封装通用的列表链接入队方法,避免重复代码
    const enqueueDetailLinks = async () => {
        await Apify.utils.enqueueLinks({
            page: page,
            requestQueue: requestQueue,
            selector: "#traziPoduzeca > tbody > tr > td > span > a",
            baseUrl: "https://www.fininfo.hr",
            // 用pseudoUrls做详情页链接匹配,替换之前错误放在baseUrl里的正则规则
            pseudoUrls: [
                "https://www.fininfo.hr/Poduzece/[.*]"
            ],
            transformRequestFunction: (req) => {
                req.userData = { label: "DETAIL" };
                return req;
            },
        });
    };

    // 先处理第一页的链接
    await enqueueDetailLinks();
    log.info('已入队当前页详情链接');

    // 循环处理分页:每次重新查找下一页按钮,从根源避免元素句柄失效问题
    while (true) {
        // 每次页面加载完重新定位下一页按钮,只取紧邻当前页的第一个下一页按钮
        const [nextBtn] = await page.$x("//div[@class='contentNav'][1]//div[@class='pagination'][1]//span[@class='current']/following-sibling::a[1]");
        
        // 找不到下一页按钮说明已经到最后一页,直接退出循环
        if (!nextBtn) {
            log.info('所有分页处理完成');
            break;
        }

        // 同时注册导航监听和点击动作,避免漏等页面跳转
        await Promise.all([
            page.waitForNavigation({ waitUntil: "domcontentloaded" }),
            nextBtn.click()
        ]);

        log.info("已跳转至下一页");
        // 等新页面加载完成后再入队当前页的详情链接
        await enqueueDetailLinks();
    }
};
额外优化点
  • 尽量不要使用固定时长的waitForTimeout做等待,固定时长要么等太久拖慢爬取速度,要么页面没加载完导致报错,优先用waitForNavigation、waitForSelector这类根据页面实际状态触发的等待方法。
  • 分页逻辑不要一次性取所有按钮,大部分网站的分页按钮是动态渲染的,跳转到下一页后页码结构会变化,提前获取的按钮完全没有复用价值,每次重新查找下一页按钮的写法稳定性最高。
  • 如果点击按钮还是会触发新标签页,可以在页面初始化阶段加一段脚本拦截target="_blank"的行为,强制所有链接在当前页打开:
    await page.evaluateOnNewDocument(() => {
        document.addEventListener('click', (e) => {
            const a = e.target.closest('a');
            if (a && a.target === '_blank') a.target = '_self';
        }, true);
    });
    

内容的提问来源于stack exchange,提问作者Adam Sever

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 23:06:23