我正在尝试从一个网站获取产品数据,该网站在用户向下滚动时加载产品列表。我使用Apify来做这件事。我的第一个想法是看看是否有人已经解决了这个问题,我找到了两个有用的链接:How to make the Apify Crawler to scroll full page when web page have infinite scrolling?和How to scrape dynamic-loading listing and individual pages using Apify?。但是,当我尝试应用他们提到的函数时,我的Apify爬虫无法加载内容。
我使用的是一个基于基本web-scraper repository代码的网络刮板。
我试图从this link中获取数据的网站。目前我只是在学习,所以我只想能够从这一个页面中获得数据,我不需要导航到其他页面。
我使用的PageFunction如下:
async function pageFunction(context) {
// Establishing uility constants to use throughout the code
const { request, log, skipLinks } = context;
const $ = context.jQuery;
const pageTitle = $('title').first().text();
context.log.info('Wait for website to render')
await context.waitFor(2000)
//Creating function to scroll the page til the bottom
const infiniteScroll = async (maxTime) => {
const startedAt = Date.now();
let itemCount = $('.upcName').length;
for (;;) {
log.info(`INFINITE SCROLL --- ${itemCount} initial items loaded ---`);
// timeout to prevent infinite loop
if (Date.now() - startedAt > maxTime) {
return;
}
scrollBy(0, 99999);
await context.waitFor(1000);
const currentItemCount = $('.upcName').length;
log.info(`INFINITE SCROLL --- ${currentItemCount} items loaded after scroll ---`);
if (itemCount === currentItemCount) {
return;
}
itemCount = currentItemCount;
}
};
context.log.info('Initiating scrolling function');
await infiniteScroll(60000);
context.log.info(`Scraping URL: ${context.request.url}`);
var results = []
$(".itemGrid").each(function() {
results.push({
name: $(this).find('.upcName').text(),
product_url: $(this).find('.nombreProductoDisplay').attr('href'),
image_url: $(this).find('.lazyload').attr('data-original'),
description: $(this).find('.block-with-text').text(),
price: $(this).find('.upcPrice').text()
});
});
return results
}我用for(;;){...}替换了while(true){...}循环,因为我得到了一个Unexpected constant condition. (no-constant-condition)ESLint错误。
此外,我还尝试改变滚动的大小和等待时间。
尽管如此,我似乎不能让爬虫给我得到超过32个结果。
有人能给我解释一下我做错了什么吗?
#更新#我一直在做这方面的工作,无法在Apify平台上工作,所以我最初的问题仍然存在。不过,我还是设法通过在pc上运行脚本来实现滚动功能。
发布于 2021-08-08 23:12:01
在这种情况下,您可以在滚动后检查加载微调器的可见性,而不是尝试计算项目的数量。
通过稍微更改代码,您可以使其如下所示:
async function pageFunction(context) {
// Establishing uility constants to use throughout the code
const { request, log, skipLinks } = context;
const $ = context.jQuery;
const pageTitle = $('title').first().text();
context.log.info('Wait for website to render')
// wait for initial listing
await context.waitFor('.itemGrid');
context.log.info(`Scraping URL: ${context.request.url}`);
let tries = 5; // keep track of the load spinner being invisible on the page
const results = new Map(); // this ensures you only get unique items
while (true) { // eslint-disable-line
log.info(`INFINITE SCROLL --- ${results.size} initial items loaded ---`);
// when the style is set to "display: none", it's hidden aka not loading any new items
const hasLoadingSpinner = $('.itemLoader[style*="none"]').length === 0;
if (!hasLoadingSpinner && tries-- < 0) {
break;
}
// scroll to page end, you can adjust the offset if it's not triggering the infinite scroll mechanism, like `document.body.scrollHeight * 0.8`
scrollTo({ top: document.body.scrollHeight });
$(".itemGrid").each(function() {
const $this = $(this);
results.set($this.find('#upcProducto').attr('value'), {
name: $this.find('.upcName').text(),
product_url: $this.find('.nombreProductoDisplay').attr('href'),
image_url: $this.find('.lazyload').data('original'),
description: $this.find('.block-with-text').text(),
price: $this.find('.upcPrice').text()
});
});
// because of the `tries` variable, this will effectively wait at least 5 seconds to consider it not loading anymore
await context.waitFor(1000);
// scroll to top, sometimes scrolling past the end of the page does not trigger the "load more" mechanism of the page
scrollTo({ top: 0 });
}
return [...results.values()]
}这种方法也适用于虚拟分页,比如React virtual或Twitter results,当DOM节点不在视区中时,它们会被移除。
使用超时是非常脆弱的,根据你的刮刀工作的快/慢,你的结果会有所不同。因此,您需要一个明确的指示,表明该页面没有提供新项目。
您还可以跟踪document.body.scrollHeight,因为它会在有新项目时更改。
https://stackoverflow.com/questions/68704587
复制相似问题