Кукольник - прокрутите вниз, пока вы не сможете больше

Я в ситуации, когда новый контент создается при прокрутке вниз. Новое содержимое имеет определенное имя класса.

Как я могу продолжать прокручивать до тех пор, пока все элементы не загрузятся? Другими словами, я хочу выйти на сцену, где, если я продолжу прокрутку вниз, ничего нового не будет загружено.

Я использовал код для прокрутки вниз, в сочетании с

await page.waitForSelector('.class_name');

Проблема с этим подходом заключается в том, что после того, как все элементы загружены, код продолжает прокручиваться, никаких новых элементов не создается, и в итоге я получаю ошибку тайм-аута.

EDIT: это код

await page.evaluate( () => {
                window.scrollBy(0, window.innerHeight);
            });
await page.waitForSelector('.class_name');

Ответ 1

Дайте этому шанс:

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);
        });
    });
}

Источник: https://github.com/chenxiaochun/blog/issues/38

Ответ 2

Прокрутить вниз до нижней части страницы можно двумя способами:

  1. используйте scrollIntoView (для прокрутки до той части страницы, которая может создать больше контента внизу) и селекторы (т.е. document.querySelectorAll('.class_name').length чтобы проверить, было ли сгенерировано больше контента)
  2. используйте scrollBy (чтобы постепенно прокручивать страницу вниз) и либо setTimeout, либо setInterval (чтобы постепенно проверить, находимся ли мы в нижней части страницы)

Вот реализация, использующая scrollIntoView и селектор (при условии, что .class_name - это селектор, в который мы прокручиваемся для получения большего количества контента) в простом JavaScript, который мы можем запустить в браузере:

Способ 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);

В этом методе мы сравниваем количество селекторов .class_name перед прокруткой (preCount) с прокруткой (postCount), чтобы убедиться, что мы находимся внизу страницы:

if (postCount > precount) {
  // NOT bottom of page
} else {
  // bottom of page
}

И вот 2 возможных реализации, использующих либо setTimeout либо setInterval с scrollBy в простом JavaScript, который мы можем запустить в консоли браузера:

Способ 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); });
}

Способ 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);

В этом методе мы сравниваем document.scrollingElement.scrollTop + window.innerHeight с document.scrollingElement.scrollHeight чтобы проверить, находимся ли мы в нижней части страницы:

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

Если какой-либо из приведенных выше 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' });
  });
}

Метод кукловода 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);
  }
}

Метод кукловода 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);
  });
}