管理木偶手的内存和性能

jer*_*ink 7 node.js web-scraping puppeteer

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

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

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

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

Tho*_*orf 8

您可能希望使用独立的浏览器创建多个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);
})();
Run Code Online (Sandbox Code Playgroud)

如果您有其他任务要做,您还可以直接将函数排队。通常,您通过完成后会关闭集群cluster.close(),但是您可以随意让它保持打开状态。您找到了另一个示例,该示例是在存储库中有请求时获取数据的集群。


Dee*_*abu 7

  • Reuse the browser and page instances instead of launching a browser each time
  • Also expose your chrome scraper to take requests from a queue rather than a rest endpoint. This would make sure chrome can take its sweet time and also if something crashes, the requests are in the queue.

Other performance related articles are,

  1. Do not render images, fonts and stylesheets if you would need only the DOM - https://www.scrapehero.com/how-to-increase-web-scraping-speed-using-puppeteer/
  2. Improving Performance - https://docs.browserless.io/blog/2019/05/03/improving-puppeteer-performance.html
  3. If you have enough time - CEF is worth another look - https://bitbucket.org/chromiumembedded/cef/src/master/ - CEF allows you to embed chromium into your own code, instead of using libraries - like puppeteer. (Puppeteer works by launching chrome on the side and communicating with it).
  4. Also check out Microsoft's Playwright before investing time into puppeteer ( https://playwright.dev/ ).

This is another example using puppeteer and generic-pool libraries.

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();
    });

}
Run Code Online (Sandbox Code Playgroud)

  • @AlexandruCancescu,使用 CEF,您可以将 chrome 引入您的程序中,这样您就可以避免进程间调用和进程边界 - 这会带来更好的性能和定制能力。 (2认同)

Gra*_*ler 6

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

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

const page = await browser.newPage();
await page.close();
Run Code Online (Sandbox Code Playgroud)

如果您决定为项目使用一个浏览器,那么我将确保实施错误处理过程,以确保如果程序崩溃,则在创建新的PageBrowserBrowserContext时停机时间最少。