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

使用Puppeteer如何无需滚动完整加载网页?

问题描述

我需要截取某网站的部分元素,已通过Xpath定位目标元素并截图,但当元素数量超过34个时,仅元素进入视口(viewport)后才会渲染,导致Xpath只能识别前34个元素。

目前使用以下代码滚动至页面底部:

async function autoScroll(page){
    await page.evaluate(async () => {
        await new Promise((resolve) => {
            var totalHeight = 0;
            var distance = 100;
            var timer = setInterval(() => {
                var scrollHeight = document.body.scrollHeight;
                window.scrollBy(0, distance);
                totalHeight += distance;

                if(totalHeight >= scrollHeight - window.innerHeight){
                    clearInterval(timer);
                    resolve();
                }
            }, 100);
        });
    });
}

但部分页面包含300余个元素,滚动耗时约30秒,且加快滚动速度会导致元素加载异常。使用waitUntil: networkidle2/1或waitForNavigation无效,因为页面初始仅加载视口内资源,滚动时才会请求后续资源。

是否存在无需滚动即可完整加载网页的方法?


补充说明(原触发加载代码翻译)

页面触发懒加载的核心逻辑翻译如下:

// 监听滚动事件,实现滚动加载
window.addEventListener('scroll', function() {
    // 计算当前滚动位置与页面底部的距离
    const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
    const clientHeight = document.documentElement.clientHeight || window.innerHeight;
    
    // 当滚动到距离底部200px以内时,触发加载更多
    if (scrollTop + clientHeight >= scrollHeight - 200) {
        loadMoreItems();
    }
});

// 加载更多元素的函数
function loadMoreItems() {
    // 检查是否正在加载中,避免重复请求
    if (window.isLoading) return;
    window.isLoading = true;
    
    // 异步请求获取更多元素数据
    fetch('/api/get-more-items', {
        method: 'POST',
        body: JSON.stringify({
            offset: window.currentItemCount,
            limit: 10
        })
    })
    .then(response => response.json())
    .then(data => {
        // 将新获取的元素渲染到页面中
        data.items.forEach(item => {
            const itemElement = document.createElement('div');
            itemElement.className = 'item';
            itemElement.innerHTML = `
                <h3>${item.title}</h3>
                <p>${item.description}</p>
            `;
            document.getElementById('item-container').appendChild(itemElement);
        });
        
        // 更新当前已加载的元素数量
        window.currentItemCount += data.items.length;
        
        // 如果没有更多数据,移除滚动监听
        if (data.items.length === 0) {
            window.removeEventListener('scroll', arguments.callee);
        }
        
        // 标记加载完成
        window.isLoading = false;
    })
    .catch(error => {
        console.error('加载更多元素失败:', error);
        window.isLoading = false;
    });
}

// 初始化加载第一页数据
window.currentItemCount = 0;
loadMoreItems();

解决方案

针对这种滚动懒加载场景,无需滚动即可加载全部元素的方法如下:

1. 直接循环调用加载函数

从补充代码可知,页面通过loadMoreItems()加载新元素,我们可以直接在Puppeteer中循环调用该函数,直到无新元素加载:

async function loadAllItems(page) {
    await page.evaluate(async () => {
        return new Promise(resolve => {
            const loadInterval = setInterval(async () => {
                // 正在加载或无更多数据时停止循环
                if (window.isLoading || document.querySelector('#item-container').dataset.hasMore === 'false') {
                    clearInterval(loadInterval);
                    resolve();
                    return;
                }
                await loadMoreItems();
            }, 500); // 间隔时间根据页面加载速度调整
        });
    });
}

2. 修改懒加载触发逻辑

直接修改页面变量,让页面"认为"已滚动到底部,或者强制循环调用加载函数:

await page.evaluate(() => {
    // 覆盖原加载函数,完成一次加载后自动继续,直到无数据
    const originalLoad = window.loadMoreItems;
    window.loadMoreItems = async function() {
        await originalLoad.call(this);
        // 根据返回数据判断是否还有更多元素
        if (window.currentItemCount < window.totalItemCount) {
            setTimeout(window.loadMoreItems, 300);
        }
    };
    // 启动加载
    window.loadMoreItems();
});

3. 直接批量请求API并渲染

如果已知加载元素的API接口,可直接请求全部数据后手动渲染到页面:

async function fetchAndRenderAllItems(page) {
    const allItems = [];
    let offset = 0;
    const limit = 10;
    
    while (true) {
        // 调用API获取数据
        const response = await page.evaluate(async (offset, limit) => {
            const res = await fetch('/api/get-more-items', {
                method: 'POST',
                body: JSON.stringify({ offset, limit })
            });
            return res.json();
        }, offset, limit);
        
        if (response.items.length === 0) break;
        
        allItems.push(...response.items);
        offset += limit;
    }
    
    // 将所有元素渲染到页面容器
    await page.evaluate((allItems) => {
        const container = document.getElementById('item-container');
        container.innerHTML = '';
        allItems.forEach(item => {
            const itemElement = document.createElement('div');
            itemElement.className = 'item';
            itemElement.innerHTML = `
                <h3>${item.title}</h3>
                <p>${item.description}</p>
            `;
            container.appendChild(itemElement);
        });
    }, allItems);
}

4. 禁用原有滚动监听并强制加载

移除页面原有的滚动监听,直接循环调用加载函数直到完成:

await page.evaluate(() => {
    // 移除原滚动监听
    const scrollHandler = window.addEventListener.toString().match(/function\s*\(\)\s*{([\s\S]*?)}/)[0];
    window.removeEventListener('scroll', eval(scrollHandler));
    
    // 循环加载所有元素
    async function loadAll() {
        if (!window.isLoading) {
            await loadMoreItems();
            if (window.currentItemCount < window.totalItemCount) {
                setTimeout(loadAll, 300);
            }
        }
    }
    loadAll();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:01:12