Puppeteer - 向下滚动,直到你不能再这样了

use*_*421 21 javascript node.js puppeteer

当我向下滚动时,我处于创建新内容的情况.新内容具有特定的类名.

如何继续向下滚动直到所有元素都已加载?换句话说,我想达到一个阶段,如果我继续向下滚动,没有新的东西会加载.

我正在使用代码向下滚动,再加上一个

await page.waitForSelector('.class_name');
Run Code Online (Sandbox Code Playgroud)

这种方法的问题是,在加载所有元素之后,代码继续向下滚动,没有创建新元素,最终我得到超时错误.

编辑:这是代码

await page.evaluate( () => {
                window.scrollBy(0, window.innerHeight);
            });
await page.waitForSelector('.class_name');
Run Code Online (Sandbox Code Playgroud)

Cor*_*ory 39

试一试:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({
        headless: false
    });
    const page = await browser.newPage();
    await page.goto('https://www.yoursite.com');
    await page.setViewport({
        width: 1200,
        height: 800
    });

    await autoScroll(page);

    await page.screenshot({
        path: 'yoursite.png',
        fullPage: true
    });

    await browser.close();
})();

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

                if(totalHeight >= scrollHeight){
                    clearInterval(timer);
                    resolve();
                }
            }, 100);
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

资料来源:https://github.com/chenxiaochun/blog/issues/38

  • 100); 太快了,它只会跳过整个自动滚动,我不得不使用 400 ......无论如何要检测一个类,在停止自动滚动之前出现的元素? (5认同)
  • @JannisIoannou:要在您的 puppeteer 实例上执行 JavaScript 代码,您可以使用评估方法。想象一下在评估中运行的代码就像在浏览器控制台中运行它一样。在这种情况下,调用评估时会自动创建“窗口”。请查看 [evaluate](https://github.com/puppeteer/puppeteer/blob/main/docs/api.md#pageevaluatepagefunction-args) 方法以获取更多上下文。 (2认同)

llo*_*bet 10

更容易:

    await page.evaluate(async () => {
      let scrollPosition = 0
      let documentHeight = document.body.scrollHeight

      while (documentHeight > scrollPosition) {
        window.scrollBy(0, documentHeight)
        await new Promise(resolve => {
          setTimeout(resolve, 1000)
        })
        scrollPosition = documentHeight
        documentHeight = document.body.scrollHeight
      }
    })
Run Code Online (Sandbox Code Playgroud)


kim*_*udi 8

向下滚动到页面底部可以通过两种方式完成:

  1. 使用scrollIntoView(滚动到页面的底部可以创建更多内容的部分)和选择器(即document.querySelectorAll('.class_name').length检查是否已生成更多内容)
  2. 使用scrollBy(以递增方式向下滚动页面)和setTimeoutsetInterval(以递增方式检查我们是否在页面底部)

这是在浏览器中运行的纯JavaScript中使用scrollIntoViewand选择器的实现(假设这.class_name是我们滚动到更多内容的选择器):

方法1:使用scrollIntoView和选择器

const delay = 3000;
const wait = (ms) => new Promise(res => setTimeout(res, ms));
const count = async () => document.querySelectorAll('.class_name').length;
const scrollDown = async () => {
  document.querySelector('.class_name:last-child')
    .scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'end' });
}

let preCount = 0;
let postCount = 0;
do {
  preCount = await count();
  await scrollDown();
  await wait(delay);
  postCount = await count();
} while (postCount > preCount);
await wait(delay);
Run Code Online (Sandbox Code Playgroud)

在此方法中,我们将比较.class_name滚动(preCount)之前和滚动()之后的选择器数量,postCount以检查我们是否位于页面底部:

if (postCount > precount) {
  // NOT bottom of page
} else {
  // bottom of page
}
Run Code Online (Sandbox Code Playgroud)

以下是两种可能的实现方式,它们可以使用setTimeoutsetInterval结合scrollBy使用,可以在浏览器控制台中运行:

方法2a:结合使用setTimeout和scrollBy

const distance = 100;
const delay = 100;
while (document.scrollingElement.scrollTop + window.innerHeight < document.scrollingElement.scrollHeight) {
  document.scrollingElement.scrollBy(0, distance);
  await new Promise(resolve => { setTimeout(resolve, delay); });
}
Run Code Online (Sandbox Code Playgroud)

方法2b:将setInterval与scrollBy一起使用

const distance = 100;
const delay = 100;
const timer = setInterval(() => {
  document.scrollingElement.scrollBy(0, distance);
  if (document.scrollingElement.scrollTop + window.innerHeight >= document.scrollingElement.scrollHeight) {
    clearInterval(timer);
  }
}, delay);
Run Code Online (Sandbox Code Playgroud)

在这种方法中,我们是在比较document.scrollingElement.scrollTop + window.innerHeightdocument.scrollingElement.scrollHeight检查我们是否在页面的底部:

if (document.scrollingElement.scrollTop + window.innerHeight < document.scrollingElement.scrollHeight) {
  // NOT bottom of page
} else {
  // bottom of page
}
Run Code Online (Sandbox Code Playgroud)

如果上面的JavaScript代码中的任何一个将页面一直向下滚动到底部,那么我们就知道它正在工作,我们可以使用Puppeteer来自动执行此操作。

以下是示例Puppeteer Node.js脚本,这些脚本将向下滚动到页面底部,并等待几秒钟,然后关闭浏览器。

木偶方法1:将scrollIntoView与选择器(.class_name)一起使用

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null,
    args: ['--window-size=800,600']
  });
  const page = await browser.newPage();
  await page.goto('https://example.com');

  const delay = 3000;
  let preCount = 0;
  let postCount = 0;
  do {
    preCount = await getCount(page);
    await scrollDown(page);
    await page.waitFor(delay);
    postCount = await getCount(page);
  } while (postCount > preCount);
  await page.waitFor(delay);

  await browser.close();
})();

async function getCount(page) {
  return await page.$$eval('.class_name', a => a.length);
}

async function scrollDown(page) {
  await page.$eval('.class_name:last-child', e => {
    e.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'end' });
  });
}
Run Code Online (Sandbox Code Playgroud)

木偶方法2a:将setTimeout与scrollBy一起使用

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null,
    args: ['--window-size=800,600']
  });
  const page = await browser.newPage();
  await page.goto('https://example.com');

  await scrollToBottom(page);
  await page.waitFor(3000);

  await browser.close();
})();

async function scrollToBottom(page) {
  const distance = 100; // should be less than or equal to window.innerHeight
  const delay = 100;
  while (await page.evaluate(() => document.scrollingElement.scrollTop + window.innerHeight < document.scrollingElement.scrollHeight)) {
    await page.evaluate((y) => { document.scrollingElement.scrollBy(0, y); }, distance);
    await page.waitFor(delay);
  }
}
Run Code Online (Sandbox Code Playgroud)

木偶方法2b:将setInterval与scrollBy一起使用

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null,
    args: ['--window-size=800,600']
  });
  const page = await browser.newPage();
  await page.goto('https://example.com');

  await page.evaluate(scrollToBottom);
  await page.waitFor(3000);

  await browser.close();
})();

async function scrollToBottom() {
  await new Promise(resolve => {
    const distance = 100; // should be less than or equal to window.innerHeight
    const delay = 100;
    const timer = setInterval(() => {
      document.scrollingElement.scrollBy(0, distance);
      if (document.scrollingElement.scrollTop + window.innerHeight >= document.scrollingElement.scrollHeight) {
        clearInterval(timer);
        resolve();
      }
    }, delay);
  });
}
Run Code Online (Sandbox Code Playgroud)


nag*_*hun 7

这里的许多解决方案都假设页面高度是恒定的。即使页面高度发生变化(例如,当用户向下滚动时加载新内容),该实现也能工作。

await page.evaluate(() => new Promise((resolve) => {
  var scrollTop = -1;
  const interval = setInterval(() => {
    window.scrollBy(0, 100);
    if(document.documentElement.scrollTop !== scrollTop) {
      scrollTop = document.documentElement.scrollTop;
      return;
    }
    clearInterval(interval);
    resolve();
  }, 10);
}));
Run Code Online (Sandbox Code Playgroud)


Edv*_*nTr 7

非常简单的解决方案

let lastHeight = await page.evaluate('document.body.scrollHeight');

    while (true) {
        await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
        await page.waitForTimeout(2000); // sleep a bit
        let newHeight = await page.evaluate('document.body.scrollHeight');
        if (newHeight === lastHeight) {
            break;
        }
        lastHeight = newHeight;
    }
Run Code Online (Sandbox Code Playgroud)


小智 7

与@EdvinTr类似的解决方案,它给了我很好的结果。滚动并与页面的 Y Offset 进行比较,非常简单。

let originalOffset = 0;
while (true) {
    await page.evaluate('window.scrollBy(0, document.body.scrollHeight)');
    await page.waitForTimeout(200);
    let newOffset = await page.evaluate('window.pageYOffset');
    if (originalOffset === newOffset) {
        break;
    }
    originalOffset = newOffset;
}
Run Code Online (Sandbox Code Playgroud)


x-m*_*gix 6

基于这个网址的答案

await page.evaluate(() => {
  window.scrollBy(0, window.innerHeight);
});
Run Code Online (Sandbox Code Playgroud)

  • `window.innerHeight` 不会一直滚动到底部,但使用 `window.scrollTo(0,window.document.body.scrollHeight)` 却可以。 (3认同)