管理 puppeteer 的内存和性能
Managing puppeteer for memory and performance
我正在使用 puppeteer 抓取一些页面,但我很好奇如何在节点应用程序的生产中管理它。我将在一天内抓取多达 500,000 页,但这些抓取作业将以随机间隔发生,因此这不是我可以完成的单个队列。
我想知道的是,打开浏览器,转到页面,然后在每次作业之间关闭浏览器是否更好?我认为哪个会慢很多,但也许可以更好地处理内存?
或者我是在应用程序启动时打开一个全局浏览器,然后转到该页面,并在我完成该页面后以某种方式转储该页面(例如,关闭 [=19= 中的所有选项卡) ], 但不关闭 chrome) 然后在我需要的时候重新打开一个新页面?这种方式看起来会更快,但可能会占用大量内存。
我从来没有用过这个库,尤其是在生产环境中,所以我不确定是否有我应该注意的地方。
如果您每天要抓取 500,000 页(大约每 0.1728 秒 抓取一页),那么我建议打开一个新页面在 现有 浏览器会话中,而不是为每个页面打开一个新的浏览器会话。
您可以使用以下方法打开和关闭 Page:
const page = await browser.newPage();
await page.close();
如果您决定为您的项目使用一个 浏览器,我会确保实施错误处理程序以确保如果程序崩溃,您在创建时的停机时间最短新的 Page、Browser 或 BrowserContext.
您可能想要创建一个包含多个具有独立浏览器的 Chromium 实例的池。这样做的好处是,当一个浏览器崩溃时,所有其他作业都可以保留 运行。一个浏览器(具有多个页面)的优点是内存少,CPU 优点是 cookie 在页面之间共享。
人偶操作实例池
图书馆 puppteer-cluster(免责声明:我是作者)为您创建了一个浏览器或页面池。它负责为您创建、错误处理、浏览器重启等。所以你可以简单地排队jobs/URLs,图书馆会处理其他一切。
代码示例
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.
时获取数据
- 重复使用浏览器和页面实例,而不是每次都启动浏览器
- 同时将您的 chrome 爬虫暴露给 从队列而不是休息端点接受请求 。这将确保 chrome 可以度过美好的时光,并且如果出现崩溃,请求会在队列中。
其他与性能相关的文章是,
- 如果您只需要 DOM - https://www.scrapehero.com/how-to-increase-web-scraping-speed-using-puppeteer/
,请不要渲染图像、字体和样式表
- 提高性能 - https://docs.browserless.io/blog/2019/05/03/improving-puppeteer-performance.html
- 如果您有足够的时间 - CEF 值得再看一遍 - https://bitbucket.org/chromiumembedded/cef/src/master/ - CEF 允许您将 Chromium 嵌入到您自己的代码中,而不是像 puppeteer 那样使用库。 (Puppeteer 的工作方式是在侧面启动 chrome 并与之通信)。
- 在花时间研究 puppeteer (https://playwright.dev/) 之前,还可以查看 Microsoft 的 Playwright。
- 这是一个实现网页抓取的教程 - 使用 k8、openfaas 和 puppeteer - https://www.openfaas.com/blog/puppeteer-scraping/
- 这是一篇关于如何使用代理服务器使用 headless chrome 和 puppeteer 进行抓取的重要文章 - https://blog.apify.com/how-to-make-headless-chrome-and-puppeteer-use-a-proxy-server-with-authentication-249a21a79212/
这是另一个使用 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();
});
}
我正在使用 puppeteer 抓取一些页面,但我很好奇如何在节点应用程序的生产中管理它。我将在一天内抓取多达 500,000 页,但这些抓取作业将以随机间隔发生,因此这不是我可以完成的单个队列。
我想知道的是,打开浏览器,转到页面,然后在每次作业之间关闭浏览器是否更好?我认为哪个会慢很多,但也许可以更好地处理内存?
或者我是在应用程序启动时打开一个全局浏览器,然后转到该页面,并在我完成该页面后以某种方式转储该页面(例如,关闭 [=19= 中的所有选项卡) ], 但不关闭 chrome) 然后在我需要的时候重新打开一个新页面?这种方式看起来会更快,但可能会占用大量内存。
我从来没有用过这个库,尤其是在生产环境中,所以我不确定是否有我应该注意的地方。
如果您每天要抓取 500,000 页(大约每 0.1728 秒 抓取一页),那么我建议打开一个新页面在 现有 浏览器会话中,而不是为每个页面打开一个新的浏览器会话。
您可以使用以下方法打开和关闭 Page:
const page = await browser.newPage();
await page.close();
如果您决定为您的项目使用一个 浏览器,我会确保实施错误处理程序以确保如果程序崩溃,您在创建时的停机时间最短新的 Page、Browser 或 BrowserContext.
您可能想要创建一个包含多个具有独立浏览器的 Chromium 实例的池。这样做的好处是,当一个浏览器崩溃时,所有其他作业都可以保留 运行。一个浏览器(具有多个页面)的优点是内存少,CPU 优点是 cookie 在页面之间共享。
人偶操作实例池
图书馆 puppteer-cluster(免责声明:我是作者)为您创建了一个浏览器或页面池。它负责为您创建、错误处理、浏览器重启等。所以你可以简单地排队jobs/URLs,图书馆会处理其他一切。
代码示例
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.
- 重复使用浏览器和页面实例,而不是每次都启动浏览器
- 同时将您的 chrome 爬虫暴露给 从队列而不是休息端点接受请求 。这将确保 chrome 可以度过美好的时光,并且如果出现崩溃,请求会在队列中。
其他与性能相关的文章是,
- 如果您只需要 DOM - https://www.scrapehero.com/how-to-increase-web-scraping-speed-using-puppeteer/ ,请不要渲染图像、字体和样式表
- 提高性能 - https://docs.browserless.io/blog/2019/05/03/improving-puppeteer-performance.html
- 如果您有足够的时间 - CEF 值得再看一遍 - https://bitbucket.org/chromiumembedded/cef/src/master/ - CEF 允许您将 Chromium 嵌入到您自己的代码中,而不是像 puppeteer 那样使用库。 (Puppeteer 的工作方式是在侧面启动 chrome 并与之通信)。
- 在花时间研究 puppeteer (https://playwright.dev/) 之前,还可以查看 Microsoft 的 Playwright。
- 这是一个实现网页抓取的教程 - 使用 k8、openfaas 和 puppeteer - https://www.openfaas.com/blog/puppeteer-scraping/
- 这是一篇关于如何使用代理服务器使用 headless chrome 和 puppeteer 进行抓取的重要文章 - https://blog.apify.com/how-to-make-headless-chrome-and-puppeteer-use-a-proxy-server-with-authentication-249a21a79212/
这是另一个使用 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();
});
}