Могу ли я открыть несколько браузеров Puppeteer? - PullRequest
1 голос
/ 07 апреля 2019

Я использую node-cron (что позволяет вам запускать скрипты cron внутри вашей нодовой программы) для запуска некоторой очистки кукловода. Сценарии иногда запускаются одновременно, а это значит, что одновременно будет открыто несколько экземпляров браузера const browser = await puppeteer.launch().

Это плохая практика? Если это так, есть ли альтернативный способ написания этого кода, который не приведет к сбою?

Спасибо за вашу помощь.

cron.schedule('*/15 * * * *', async () => {    
    const browser = await pupeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox']});
    const page = await browser.newPage(); // Create new instance of puppet
    let today = moment();        
    logger.info(`Chrome Launched...`);

    try {
        await senatorBot(users, page, today.format("YYYY-DD-MM"));
    } catch(err) {
        logger.debug(JSON.stringify(err));
    }

    try {
        await senateCandidateBot(users, page, today.format("YYYY-DD-MM")); // This sequence matters, because agree statement will not be present...
    } catch(err) {
        logger.debug(JSON.stringify(err));
    }

    await page.close();
    await browser.close();
    logger.info(`Chrome Closed.`);

});

cron.schedule('*/15 17-19 * * 1-5', async () => {   

    logger.info(`Chrome Launched...`); 
    const browser = await pupeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox']});
    const page = await browser.newPage(); // Create new instance of puppet
    let today = moment();

    try {
        await contractBot(users, page, today.format("MM-DD-YYYY"));
    } catch(err) {
        logger.debug(JSON.stringify(err));
    }

    await page.close();
    await browser.close();
    logger.info(`Chrome Closed.`);
});

Ответы [ 2 ]

1 голос
/ 07 апреля 2019

В общем, нет проблем открыть два браузера параллельно, если у вас достаточно мощный компьютер. Так что ответ на это полностью зависит от ресурсов вашей машины. Достаточно ли у вас памяти и процессора для работы нескольких открытых браузеров Chrome?

Проверьте, достаточно ли у вас ресурсов

Если вы используете Linux, откройте инструмент, например htop , чтобы проверить, сколько памяти и ЦП обрабатывается при выполнении задач. Когда вы превышаете пределы вашего ЦП / памяти, вам следует рассмотреть возможность выполнения задач последовательно (см. Ниже).

Использование пула ресурсов

Даже если у вас достаточно ресурсов, вы можете использовать библиотеку puppeteer-cluster (отказ от ответственности: я автор), чтобы позаботиться об обработке параллелизма. Библиотека также позаботится об обработке ошибок (что, если в браузере произойдет сбой?) И может показать вам память, использование ЦП и статистику сканирования во время выполнения.

Пример кода

Вот минимальный пример, как вы могли бы его использовать.

const { Cluster } = require('puppeteer-cluster');

async function task1({ page }) => { // your first task, page is provided to your task
    await page.goto('...');
    // ...
}

async function task2({ page }) => { // another task
    await page.goto('...');
    // ...
}

(async () => {
    const cluster = await Cluster.launch({
        concurrency: Cluster.CONCURRENCY_BROWSER, // spawn to parallel browsers
        maxConcurrency: 2, // how many tasks should be run concurrently
    });

    cron.schedule('...', () => {
        cluster.queue(task1);
    });

    cron.schedule('...', () => {
        cluster.queue(task2);
    });
})();

Ползать последовательно

Если на вашем компьютере нет ресурсов для двух запущенных браузеров, вы также можете запускать задачи одну за другой, вам нужно будет установить значение maxConcurrency в 1. Тогда поставленные в очередь задачи будут выполняться не параллельно, а последовательно, поскольку существует только один открытый ресурс.

1 голос
/ 07 апреля 2019

Хорошо, нашел кусочки кода, чтобы вы начали. Код привязан к моей пользовательской базе кода, но функции, которые я использую, могут быть легко заменены вашими собственными.

Итак, во-первых, я пишу файл простого узла, который создает экземпляр Chromium и сохраняет ссылку на wsEndpoint, который мы затем можем использовать для подключения.

файл: chromiumLauncher.js

const writeText = require("mylib/core.io.file/write-text");
const puppeteer = require("puppeteer");
const path = require("path");
const common = require("./common");

(async () => {
  const launch_options = {
    args: ['--disable-features=site-per-process'],
    headless: false,
    devtools: false,
    defaultViewport: {width: 1200, height: 1000},
    userDataDir: common.userDataDir
  };
  const browser = await puppeteer.launch(launch_options);
  const wsEndpoint = browser.wsEndpoint();
  await writeText(common.fnSettings, JSON.stringify({wsEndpoint}, null, "  "));
})();

В вышеупомянутом common.js я просто храню некоторые простые настройки конфигурации, вы можете заменить их своими, он просто хранит некоторые пути, он просто хранит, где pupperteer размещает свои файлы данных и где сохранять wsEndpoint значение. А write-text - это просто простая функция, основанная на обещаниях для записи текстовых файлов, в основном fs.writeFile с кодировкой, установленной на utf-8.

Затем мы просто создадим еще один js-файл с именем connect,

const puppeteer = require("puppeteer");
const cp = require('child_process');
const delay = require("mylib/promise/delay");
let browser = null;

const readText = require("mylib/core.io.file/read-text");
const common = require("./common");


async function launch () {
  cp.spawn('node', ['chromiumLauncher.js'], {
    detached: true,
    shell: true,
    cwd: __dirname
  });
  await delay(5000); //lets wait 5 seconds
}

async function getSettings() {
  try {
    const settingsTxt = await readText(common.fnSettings);
    return JSON.parse(settingsTxt);
  } catch (e) {
    if (e.code !== 'ENOENT') throw e;
    return null;
  }
}


async function connect () {
  if (browser) return browser;
  let settings = await getSettings();
  if (!settings) {
    await launch();
    settings = await getSettings();
  }
  try {
    browser = await puppeteer.connect({browserWSEndpoint: settings.wsEndpoint});
  } catch (e) {
    const err = e.error || e;
    if (err.code === "ECONNREFUSED") {
      console.log("con ref");
      await launch();
      settings = await getSettings();
      browser = await puppeteer.connect({browserWSEndpoint: settings.wsEndpoint});
    }
  }
  return browser;
}


module.exports = connect;

Снова пара пользовательских библиотечных функций, описанных выше, но их легко заменить. read-text, прямо противоположное тексту записи, а delay просто задержка на основе обещания.

И это все, чтобы использовать ..

const connect = require("path-to/connect");
const browser = await connect();
const page = await browser.newPage();

И поскольку мы запускаем Chromium отделенным, когда процессы закрываются / соединяются, он остается открытым между ними. У меня было около 7 процессов, связанных с 70 веб-страницами, открытыми в Chromium без каких-либо проблем. Следует отметить одну вещь: поскольку я запускаю хром внутри отдельного спавна, вам остается вручную закрыть хром, если вам это тоже нужно. Другой вариант - просто запустить chromiumLauncher.js в каком-нибудь диспетчере процессов, например PM2 https://www.npmjs.com/package/pm2,

...