管理木偶手的内存和性能

时间:2018-08-22 16:56:46

标签: node.js web-scraping puppeteer

我正在使用puppeteer抓取一些页面,但是我很好奇如何在生产中为节点应用程序管理此页面。我一天最多要抓取500,000页,但是这些抓取作业将以随机间隔进行,因此我无法耕种一个队列。

我想知道的是,打开浏览器,转到页面,然后在每个作业之间关闭浏览器会更好吗?我以为会慢很多,但也许可以更好地处理内存?

还是在应用启动时打开一个全局浏览器,然后转到页面,并用某种方式转储该页面(例如,关闭chrome中的所有标签,但不关闭chrome) ),然后在需要时重新打开一个新页面?这样看来会更快,但可能会消耗大量内存。

我从来没有使用过这个库,尤其是在生产环境中,所以我不确定是否需要注意。

3 个答案:

答案 0 :(得分:3)

如果每天要抓取 500,000页(每 0.1728秒大约一页),那么我建议您在现有的中打开一个新页面em>浏览器会话,而不是为每个页面打开新的浏览器会话。

您可以使用以下方法打开和关闭Page

const page = await browser.newPage();
await page.close();

如果您决定在项目中使用一个Browser,我将确保实施错误处理过程,以确保如果程序崩溃,则在创建新的Page时停机时间最少, BrowserBrowserContext

答案 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)

  • 重用浏览器和页面实例,而不是每次都启动浏览器
  • 还公开了您的Chrome抓取工具,以从队列而不是其他端点接收请求。这将确保chrome可以度过美好的时光,并且如果发生崩溃,请求也将排在队列中。

其他与性能相关的文章

  1. 如何不呈现图像,字体和样式表,https://www.scrapehero.com/how-to-increase-web-scraping-speed-using-puppeteer/
  2. 提高性能-https://docs.browserless.io/blog/2019/05/03/improving-puppeteer-performance.html

这是另一个使用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();
    });

}