完成所有操作后再进行一次刮擦的最佳方法

Best way to push one more scrape after all are done

我有以下场景:

目前,我的请求队列中有所有 30 个 url(通过 Apify 网络界面),我正在尝试查看它们何时全部完成。

但显然它们都是 运行 异步的,因此数据永远不可靠

 const queue = await Apify.openRequestQueue();
 let  pendingRequestCount  = await queue.getInfo();

我需要将最后一个 URL 分开的原因有两个:

  1. 最明显的原因是我需要确定我有 在我将所有内容发送到 DB
  2. 之前所有 30 次擦除的结果
  3. 30 个 URL 都不允许我进行 Ajax / Fetch 调用,这 我需要发送到 Firebase 并进行地址的地理查找

编辑:根据@Lukáš Křivka 的回答进行了尝试。 while 循环中的 handledRequestCount 达到最大值 2,而不是 4 ...,Puppeteer 正常结束。我将 "return" 放在 while 循环中,否则请求永远不会完成(当然)。

在我当前的测试设置中,我有 4 个 url 要被抓取(在 Puppeteer Scraper 的 Start URLS 输入字段中(在 Apify.com 上)和此代码 :

let title = "";
const queue = await Apify.openRequestQueue();
let {handledRequestCount} = await queue.getInfo();
while (handledRequestCount < 4){
    await new Promise((resolve) => setTimeout(resolve, 2000)) // wait for 2 secs
    handledRequestCount = await queue.getInfo().then((info) => info.handledRequestCount);
    console.log(`Curently handled here: ${handledRequestCount} --- waiting`) // this goes max to '2'
    title = await page.evaluate(()=>{ return $('h1').text()});
    return {title};
}
log.info("Here I want to add another URL to the queue where I can do ajax stuff to save results from above runs to firebase db");
title = await page.evaluate(()=>{ return $('h1').text()});
return {title};

我需要查看您的代码才能完全正确地回答,但这有解决方案。

只需对 30 URL 使用 Apify.PuppeteerCrawler。然后你 运行 爬虫 await crawler.run().

之后,您可以通过

从默认数据集中加载数据
const dataset = await Apify.openDataset();
const data = await dataset.getdata().then((response) => response.items);

对数据做任何事情,您甚至可以创建新的 Apify.PuppeteerCrawler 来抓取最后一个 URL 并使用数据。

不过,如果您使用的是 Web Scraper,那就有点复杂了。您可以:

1) 为 Firebase 上传创建一个单独的 actor,并从 Web Scraper 向其传递一个 webhook 以从中加载数据。如果您查看 Apify store,我们已经有一个 Firestore 上传器。

2) 添加一个逻辑,该逻辑将像您所做的那样轮询 requestQueue,并且只有在处理完所有请求后,您才继续。您可以创建某种等待的循环。例如

const queue = await Apify.openRequestQueue();
let { handledRequestCount } = await queue.getInfo();
while (handledRequestCount < 30) {
    console.log(`Curently handled: ${handledRequestCount } --- waiting`)
    await new Promise((resolve) => setTimeout(resolve, 2000)) // wait for 2 secs
    handledRequestCount = await queue.getInfo().then((info) => info.handledRequestCount);
}
// Do your Firebase stuff

如果你有一个异步函数调用所有 30 个你抓取的 URL,首先确保函数 returns 在所有必要的等待之后它的结果,你可以等待 Promise.all(arrayOfAll30Promises) 然后运行 你的最后一段代码

因为我无法从 getInfo() 获得与 {handledRequestCount} 一致的结果(请参阅我在原始问题中的编辑),所以我选择了另一条路线。

我基本上是在记录哪些 URL 已经通过 key/value 商店被抓取了。

 urls = [
   {done:false, label:"vietnam", url:"https://en.wikipedia.org/wiki/Vietnam"},
   {done:false , label:"cambodia", url:"https://en.wikipedia.org/wiki/Cambodia"}
 ]

 // Loop over the array and add them to the Queue
 for (let i=0; i<urls.length; i++) {
   await queue.addRequest(new Apify.Request({ url: urls[i].url }));
 }

 // Push the array to the key/value store with key 'URLS'
 await Apify.setValue('URLS', urls);

现在每次处理 url 时,我都会将其 "done" 值设置为 true。 当它们都为真时,我将另一个(最终)url 推入队列:

 await queue.addRequest(new Apify.Request({ url: "http://www.placekitten.com" }));