我正在使用节点 v20.15.0 并有一个 puppeteer 文件,该文件会转到一个页面,该页面包含大约 200-300 个图像,当您向下滚动页面时,这些图像会延迟加载。
我创建了一些可以在浏览器中运行的js,如果我直接在浏览器中运行它,但是当我尝试在 page.evaluate 中运行相同的东西时,setTimeout 将被忽略。
基本上它会将页面滚动 500 像素,等待 1.25 秒让所有内容加载,然后使用 setTimeout 递归调用scroll_page 再次滚动。由于某种原因,它只是跳过 page.evaluate 内的 setTimeout 调用并退出 page.evaluate 并在此之后继续。
var browser;
var puppeteer_options = {
headless: true,
ignoreHTTPSErrors: true,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--single-process', '--no-zygote', '--disable-dev-shm-usage', '--shm-size=4gb'],
defaultViewport: {width: 1920, height: 1080},
}
browser = await puppeteer.launch(puppeteer_options);
var page = await browser.newPage();
var url = gp_args['url'];
await page.goto(url, {waitUntil: 'networkidle2'});
await page.setDefaultNavigationTimeout(0)
//enable logging inside page.evaluate
await page.exposeFunction('logInNodeJs', (value) => console.log(value));
//scroll down the page to get all html
var retval = await page.evaluate(async () => {
var retval = {}
var el = document.documentElement
logInNodeJs('scrollHeight: ' + el.scrollHeight)
var cur_scroll_top = el.scrollTop
logInNodeJs('cur_scroll_top start: ' + cur_scroll_top)
var prev_scroll_top = cur_scroll_top
logInNodeJs('prev_scroll_top start: ' + prev_scroll_top)
scroll_page({
el: el,
cur_scroll_top: cur_scroll_top,
prev_scroll_top: prev_scroll_top,
safety: 0,
})
function scroll_page(options)
{
var el = options.el
var cur_scroll_top = options.cur_scroll_top
var prev_scroll_top = options.prev_scroll_top
var safety = options.safety
el.scrollTop += 500
var cur_scroll_top = el.scrollTop
logInNodeJs('cur_scroll_top: ' + cur_scroll_top + ' previous: ' + prev_scroll_top)
if(cur_scroll_top == prev_scroll_top)
{
logInNodeJs('end reached!')
//end_page_flag = true
}
else
{
var prev_scroll_top = cur_scroll_top
logInNodeJs('prev_scroll_top: ' + prev_scroll_top)
safety += 1
if(safety < 20)
{
setTimeout(function(){scroll_page({
el: el,
cur_scroll_top: cur_scroll_top,
prev_scroll_top: prev_scroll_top,
safety: safety,
})}, 1250)
}
}
}
return retval
})
“安全”只是为了调试,试图防止无限循环的发生,也为了让它在测试时运行得更快。当我在浏览器中运行它时,没有使用安全性,并且它运行得很好。
还有其他方法可以在 page.evaluate 中递归调用scroll_page 吗?
这里有很多问题,但我将添加一个简单的示例来说明您的主要问题。
evaluate
等待从回调返回的任何 Promise,但是如果您没有从回调返回 Promise,则它不会等待。 evaluate
不知道您触发了 setTimeout
——异步回调没有与主 Promise 链链接,因此无法等待它。
您可以将其与循环和承诺的超时连接起来:
import puppeteer from "puppeteer"; // ^22.7.1
const html = `<!DOCTYPE html><html><body>
<script>
for (let i = 0; i < 100; i++) {
document.body.innerHTML += '<h1>hello world</h1>';
}
document.body.innerHTML += '<h1>BOTTOM</h1>';
</script>
</body></html>`;
let browser;
(async () => {
browser = await puppeteer.launch({headless: false});
const [page] = await browser.pages();
// https://stackoverflow.com/a/60075804
page.on('console', async e => {
const args = await Promise.all(e.args().map(a => a.jsonValue()));
console.log(...args);
});
await page.setContent(html);
await page.evaluate(async () => {
const doc = document.documentElement;
for (;;) {
console.log(doc.scrollTop);
const lastScrollTop = doc.scrollTop;
doc.scrollTop += 500;
if (lastScrollTop === doc.scrollTop) {
break;
}
await new Promise(r => setTimeout(r, 1250));
}
});
console.log("done");
await page.screenshot({path: "proof.png"});
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
退一步讲,使用 Puppeteer 滚动可能会更好——只需按向下翻页键即可。