Я нахожусь в ситуации, когда при прокрутке вниз создается новый контент. Новый контент имеет определенное имя класса.
Как я могу прокручивать вниз, пока не загрузятся все элементы?
Другими словами, я хочу достичь стадии, когда, если я продолжу прокручивать вниз, ничего нового не загрузится.
Я использовал код для прокрутки вниз вместе с
await page.waitForSelector('.class_name');
Проблема с этим подходом заключается в том, что после загрузки всех элементов код продолжает прокручиваться вниз, новые элементы не создаются, и в конечном итоге я получаю ошибку тайм-аута.
Это код:
await page.evaluate( () => {
window.scrollBy(0, window.innerHeight);
});
await page.waitForSelector('.class_name');
if i keep scrolling down, nothing new will load Определите «ничего нового не загружается» и проверьте это в своем коде. Также можно переопределить таймауты. Но да, Грант Миллер прав, пожалуйста, предоставьте свой код и, в идеале, URL-адрес целевого сайта.
Большое спасибо! Я обновил код. Поскольку это локальный сайт, я не могу опубликовать URL-адрес ... «Ничего нового не загрузится» означает, что веб-сайт загрузил все доступные элементы, и поэтому, когда я продолжаю прокручивать вниз и использовать page.waitForSelector (), новых появятся элементы, и мой код будет бесконечно ждать, пока не выдаст ошибку тайм-аута.
вы можете попробовать этот await page.evaluate('window.scrollTo(0, document.body.scrollHeight)')



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Попробуйте это:
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 ... есть ли способ обнаружить класс, элемент, появляющийся перед остановкой автопрокрутки?
Когда вы evaluateing, у вас есть ссылка на контекст документа. Таким образом, вы должны просто использовать стандартный селектор и проверить его положение с помощью getBoundingClientRect.
@CodeGuru можно остановить автопрокрутку, используя имя класса, но вам нужно использовать scrollIntoView вместо scrollBy, что означает, что вам нужна ссылка на элемент для прокрутки, который, возможно, создаст больше контента в нижней части страницы. Затем вы можете сравнить количество имен классов перед прокруткой в представление и после прокрутки в представление. Если количество имен классов увеличивается после прокрутки до представления, создается больше контента, поэтому вы можете прокручивать больше. В противном случае контент больше не создается, поэтому прекратите прокрутку. Надеюсь, это имеет смысл.
Ошибка: ошибка протокола (Runtime.callFunctionOn): цель закрыта. Я прокручиваю страницу, которая загружает новые данные каждый раз, когда вы прокручиваете вниз, некоторое время работает, но через некоторое время появляется эта ошибка.
Это отлично работает, когда я запускаю его локально на моем компьютере с Windows. Но когда закачиваю на свой linux vps. Он не загружает больше элементов, чем исходные.
lqbal: Это может быть связано с вашим xvfb. Попробуйте сменить headless: false на headless: true
Где определяется window?
@JannisIoannou, взгляни на этот MDN. window - это глобальный объект браузера, представляющий окно, в котором выполняется скрипт. Если вы ссылаетесь на window в Node, вы получите сообщение об ошибке.
@Cory Я говорю о контексте узла, откуда запускается кукловод. Где определено окно в приведенном выше коде node?
@JannisIoannou: чтобы выполнить код JavaScript в экземпляре кукольника, вы используете метод оценки. Представьте, что код, работающий внутри, оценивается так, как если бы вы запускали его в консоли браузера. В этом случае window создается автоматически при вызове eval. Пожалуйста, взгляните на метод оценивать для дополнительного контекста.
Прокрутку вниз страницы можно выполнить двумя способами:
document.querySelectorAll('.class_name').length, чтобы проверить, было ли создано больше контента)Вот реализация с использованием 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) это происходит.
Вы должны спросить себя, прокручиваете ли вы элемент, который требует от страницы отложенной загрузки данных до достижения этой 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);
Это похоже на принятый ответ, который на самом деле цитирует код.
Вы можете просто использовать следующий код, используя объект 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);
});
}
Только когда у нас есть эти кнопки вверх и вниз.
Многие решения здесь предполагают постоянную высоту страницы. Эта реализация работает, даже если высота страницы изменяется (например, загружается новый контент, когда пользователь прокручивает страницу вниз).
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);
}));
Для страниц с измененной высотой эта функция разрешается быстрее ...
Довольно простое решение
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
}
})
Похоже, что с кодом, который вы используете для прокрутки вниз, может быть проблема. Не могли бы вы добавить это к своему вопросу?