我正在使用puppeteer抓取一些页面,但是我很好奇如何在生产中为节点应用程序管理此页面。我一天最多要抓取500,000页,但是这些抓取作业将以随机间隔进行,因此我无法耕种一个队列。
我想知道的是,打开浏览器,转到页面,然后在每个作业之间关闭浏览器会更好吗?我以为会慢很多,但也许可以更好地处理内存?
还是在应用启动时打开一个全局浏览器,然后转到页面,并用某种方式转储该页面(例如,关闭chrome中的所有标签,但不关闭chrome) ),然后在需要时重新打开一个新页面?这样看来会更快,但可能会消耗大量内存。
我从来没有使用过这个库,尤其是在生产环境中,所以我不确定是否需要注意。
答案 0 :(得分:3)
如果每天要抓取 500,000页(每 0.1728秒大约一页),那么我建议您在现有的中打开一个新页面em>浏览器会话,而不是为每个页面打开新的浏览器会话。
您可以使用以下方法打开和关闭Page:
const page = await browser.newPage();
await page.close();
如果您决定在项目中使用一个Browser,我将确保实施错误处理过程,以确保如果程序崩溃,则在创建新的Page时停机时间最少, Browser或BrowserContext。
答案 1 :(得分:3)
您可能想使用独立的浏览器创建多个Chromium实例的池。这样做的好处是,当一个浏览器崩溃时,所有其他作业都可以继续运行。一个浏览器(具有多个页面)的优势是内存和CPU优势较小,并且cookie在页面之间共享。
puppteer-cluster库(免责声明:我是作者)为您创建了一个浏览器或页面池。它将为您完成创建,错误处理,浏览器重启等工作。因此,您只需将作业/ URL排队即可,而库则负责其他所有工作。
const { Cluster } = require('puppeteer-cluster');
(async () => {
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_BROWSER, // use one browser per worker
maxConcurrency: 4, // cluster with four workers
});
// Define a task to be executed for your data (put your "crawling code" in here)
await cluster.task(async ({ page, data: url }) => {
await page.goto(url);
// ...
});
// Queue URLs when the cluster is created
cluster.queue('http://www.google.com/');
cluster.queue('http://www.wikipedia.org/');
// Or queue URLs anytime later
setTimeout(() => {
cluster.queue('http://...');
}, 1000);
})();
如果您要执行其他任务,也可以直接将功能排队。通常,通过cluster.close()
完成操作后,您将关闭集群,但是您可以随意使其保持打开状态。在repository中,您找到了另一个集群的示例,该集群在请求进入时获取数据。
答案 2 :(得分:1)
其他与性能相关的文章
这是另一个使用puppeteer和通用池库的示例。
const puppeteer = require('puppeteer');
const genericPool = require("generic-pool");
async function createChromePool() {
const factory = {
create: function() {
//open an instance of the Chrome headless browser - Heroku buildpack requires these args
return puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox', '--ignore-certificate-errors'] });
},
destroy: function(client) {
//close the browser
client.close();
}
};
const opts = { max: 1, acquireTimeoutMillis: 120000, priorityRange: 3};
global.chromepool = genericPool.createPool(factory, opts);
global.chromepool.on('factoryCreateError', function(err){
debug(err);
});
global.chromepool.on('factoryDestroyError', function(err){
debug(err);
});
}
async function destroyChromePool() {
// Only call this once in your application -- at the point you want to shutdown and stop using this pool.
global.chromepool.drain().then(function() {
global.chromepool.clear();
});
}