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

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

Как я могу прокручивать вниз, пока не загрузятся все элементы?

Другими словами, я хочу достичь стадии, когда, если я продолжу прокручивать вниз, ничего нового не загрузится.

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

await page.waitForSelector('.class_name');

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

Это код:

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

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

Grant Miller 26.07.2018 05:23
if i keep scrolling down, nothing new will load Определите «ничего нового не загружается» и проверьте это в своем коде. Также можно переопределить таймауты. Но да, Грант Миллер прав, пожалуйста, предоставьте свой код и, в идеале, URL-адрес целевого сайта.
Vaviloff 26.07.2018 10:28

Большое спасибо! Я обновил код. Поскольку это локальный сайт, я не могу опубликовать URL-адрес ... «Ничего нового не загрузится» означает, что веб-сайт загрузил все доступные элементы, и поэтому, когда я продолжаю прокручивать вниз и использовать page.waitForSelector (), новых появятся элементы, и мой код будет бесконечно ждать, пока не выдаст ошибку тайм-аута.

user1584421 26.07.2018 11:57

вы можете попробовать этот await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')

Ondrej Kvasnovsky 16.10.2018 19:53
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
61
4
59 012
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Попробуйте это:

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

100); слишком быстро, он просто пропустил бы всю автопрокрутку, мне пришлось использовать 400 ... есть ли способ обнаружить класс, элемент, появляющийся перед остановкой автопрокрутки?

CodeGuru 12.01.2019 16:56

Когда вы evaluateing, у вас есть ссылка на контекст документа. Таким образом, вы должны просто использовать стандартный селектор и проверить его положение с помощью getBoundingClientRect.

Cory 14.01.2019 23:02

@CodeGuru можно остановить автопрокрутку, используя имя класса, но вам нужно использовать scrollIntoView вместо scrollBy, что означает, что вам нужна ссылка на элемент для прокрутки, который, возможно, создаст больше контента в нижней части страницы. Затем вы можете сравнить количество имен классов перед прокруткой в ​​представление и после прокрутки в представление. Если количество имен классов увеличивается после прокрутки до представления, создается больше контента, поэтому вы можете прокручивать больше. В противном случае контент больше не создается, поэтому прекратите прокрутку. Надеюсь, это имеет смысл.

kimbaudi 17.06.2019 11:55

Ошибка: ошибка протокола (Runtime.callFunctionOn): цель закрыта. Я прокручиваю страницу, которая загружает новые данные каждый раз, когда вы прокручиваете вниз, некоторое время работает, но через некоторое время появляется эта ошибка.

Omar 04.08.2019 20:51

Это отлично работает, когда я запускаю его локально на моем компьютере с Windows. Но когда закачиваю на свой linux vps. Он не загружает больше элементов, чем исходные.

Iqbal 30.09.2019 19:35

lqbal: Это может быть связано с вашим xvfb. Попробуйте сменить headless: false на headless: true

Cory 03.10.2019 02:53

Где определяется window?

Jannis Ioannou 06.04.2021 01:48

@JannisIoannou, взгляни на этот MDN. window - это глобальный объект браузера, представляющий окно, в котором выполняется скрипт. Если вы ссылаетесь на window в Node, вы получите сообщение об ошибке.

Cory 06.04.2021 19:27

@Cory Я говорю о контексте узла, откуда запускается кукловод. Где определено окно в приведенном выше коде node?

Jannis Ioannou 06.04.2021 20:35

@JannisIoannou: чтобы выполнить код JavaScript в экземпляре кукольника, вы используете метод оценки. Представьте, что код, работающий внутри, оценивается так, как если бы вы запускали его в консоли браузера. В этом случае window создается автоматически при вызове eval. Пожалуйста, взгляните на метод оценивать для дополнительного контекста.

Cory 22.04.2021 19:52

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

  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
}

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

Метод 2а: используйте 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' });
  });
}

Метод кукловода 2а: используйте 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);
  });
}

на основе ответа от этого url

await page.evaluate(() => {
  window.scrollBy(0, window.innerHeight);
});
window.innerHeight не прокручивается до конца, но с window.scrollTo(0,window.document.body.scrollHeight) это происходит.
K. Frank 16.11.2020 04:43

Вы должны спросить себя, прокручиваете ли вы элемент, который требует от страницы отложенной загрузки данных до достижения этой DOM. Например, эта страница sephora: https://www.sephora.com/search?keyword=clean%20at%20sephora

Если это так, вам нужно дождаться загрузки обещания, прежде чем переходить, например, к нижнему колонтитулу, а scrollToElement, как и приведенные выше решения, без использования обещания не приведет вас к концу элемента.

В этом случае вам необходимо ввести Promise внутрь page.evaluate.

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);
    });
  });
}
await autoScroll(page);

Это похоже на принятый ответ, который на самом деле цитирует код.

ggorlen 04.02.2021 01:08

Вы можете просто использовать следующий код, используя объект page.keyboard:

await page.keyboard.press('ArrowDown');
delay(2000) //wait for 2 seconds
await page.keyboard.press('ArrowUp');
function delay(milliseconds) { //function for waiting
        return new Promise(resolve => {
          setTimeout(() => {
            resolve();
          }, milliseconds);
        });
      }

Только когда у нас есть эти кнопки вверх и вниз.

Kapil Raghuwanshi 28.05.2020 10:00

Многие решения здесь предполагают постоянную высоту страницы. Эта реализация работает, даже если высота страницы изменяется (например, загружается новый контент, когда пользователь прокручивает страницу вниз).

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

Для страниц с измененной высотой эта функция разрешается быстрее ...

Raunaqss 11.06.2021 15:52

Довольно простое решение

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

Намного легче:

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

Другие вопросы по теме