2

我有以下情况:

  • 我的擦伤在登录后面,所以我总是需要先点击一个登录页面
  • 然后我有一个 30 个 url 的列表,可以异步抓取,我只关心
  • 然后在最后,当所有这 30 个 url 都被抓取后,我需要点击最后一个单独的 url,将 30 个 URL 抓取的结果放入 firebase 数据库并进行一些其他突变(如地址的地理查找等)

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

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

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

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

  1. 最明显的原因是,在将所有内容发送到数据库之前,我需要确保获得所有 30 次刮擦的结果
  2. 30 个 URL 中的任何一个都不允许我进行 Ajax / Fetch 调用,我需要将其发送到 Firebase 并进行地址的地理查找

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

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

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

3 回答 3

0

在您有一个为您抓取的所有 30 个 URL 调用的异步函数的场景中,首先确保该函数在所有必要的等待之后返回其结果,您可以等待Promise.all(arrayOfAll30Promises)然后运行最后一段代码

于 2019-08-13T09:16:57.247 回答
0

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

我基本上记录了哪些 URL 已经通过键/值存储被抓取。

 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" }));
于 2019-08-13T21:14:47.593 回答
0

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

只需对 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 上传创建一个单独的参与者,并从您的 Web Scraper 传递一个 webhook 以从中加载数据。如果您查看Apify 商店,我们已经有一个 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
于 2019-08-13T09:11:41.760 回答