完成所有操作后再进行一次刮擦的最佳方法
Best way to push one more scrape after all are done
我有以下场景:
- 我的爬虫需要登录,所以我总是需要先点击一个登录页面
- 然后我有一个包含 30 个 url 的列表,可以异步抓取所有我关心的
- 然后在最后,当所有这 30 个 url 都被抓取后,我需要点击最后一个单独的 url 将 30 个 URL 抓取的结果放入一个 firebase 数据库并做一些其他的突变(比如地址的地理查找等)
目前,我的请求队列中有所有 30 个 url(通过 Apify 网络界面),我正在尝试查看它们何时全部完成。
但显然它们都是 运行 异步的,因此数据永远不可靠
const queue = await Apify.openRequestQueue();
let pendingRequestCount = await queue.getInfo();
我需要将最后一个 URL 分开的原因有两个:
- 最明显的原因是我需要确定我有
在我将所有内容发送到 DB
之前所有 30 次擦除的结果
- 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" }));
我有以下场景:
- 我的爬虫需要登录,所以我总是需要先点击一个登录页面
- 然后我有一个包含 30 个 url 的列表,可以异步抓取所有我关心的
- 然后在最后,当所有这 30 个 url 都被抓取后,我需要点击最后一个单独的 url 将 30 个 URL 抓取的结果放入一个 firebase 数据库并做一些其他的突变(比如地址的地理查找等)
目前,我的请求队列中有所有 30 个 url(通过 Apify 网络界面),我正在尝试查看它们何时全部完成。
但显然它们都是 运行 异步的,因此数据永远不可靠
const queue = await Apify.openRequestQueue();
let pendingRequestCount = await queue.getInfo();
我需要将最后一个 URL 分开的原因有两个:
- 最明显的原因是我需要确定我有 在我将所有内容发送到 DB 之前所有 30 次擦除的结果
- 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" }));