我可以打开多个 Puppeteer 浏览器吗?
Can I have multiple Puppeteer browsers open?
我正在使用 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.`);
});
好的,找到了让您入门的代码。代码绑定到我的自定义代码库中,但我使用的功能可以很容易地替换为您自己的。
所以首先,我编写了一个简单的节点文件来创建一个 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
.
接下来我们创建另一个名为 connect
,
的 js 文件
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
,与 write-text 相反,delay
只是一个简单的基于承诺的延迟。
就是这样,使用..
const connect = require("path-to/connect");
const browser = await connect();
const page = await browser.newPage();
并且因为我们启动 Chromium 是分离的,当进程关闭/连接时,它会保持打开状态。我有大约 7 个进程与在 Chromium 中打开的 70 个网页相关联,没有任何问题。需要注意的一件事是,因为我确实在分离的 spawn 中启动了 chromium,所以如果你也需要的话,你可以手动关闭 chromium。另一种选择是在某些进程管理器中启动 chromiumLauncher.js
,例如 PM2 https://www.npmjs.com/package/pm2、
一般情况下,只要你的机器足够强大,同时打开两个浏览器是没有问题的。所以这个问题的答案完全取决于你机器的资源。您是否有足够的内存和 CPU 来支持多个打开的 Chrome 浏览器?
检查您是否有足够的资源
如果您正在使用 linux,请打开 htop 之类的工具来检查当任务 运行 时处理了多少内存和 CPU。当您达到 CPU/memory 极限时,您应该考虑 运行 按顺序执行任务(见下文)。
使用资源池
即使您有足够的资源,您也可以使用库 puppeteer-cluster(免责声明:我是作者)来处理并发处理。该库还将负责错误处理(如果浏览器崩溃怎么办?)并可以向您显示 运行.
期间的内存、CPU 使用情况和抓取统计信息
代码示例
这是一个简单的示例,您可以如何使用它。
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);
});
})();
按顺序抓取
如果你的机器没有足够的资源来安装两个 运行ning 浏览器,你也可以 运行 一个接一个地执行任务,你只需要设置 [=11] 的值=] 到 1
。那么排队的任务将不会运行并行,而是顺序,因为只有一个打开的资源。
我正在使用 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.`);
});
好的,找到了让您入门的代码。代码绑定到我的自定义代码库中,但我使用的功能可以很容易地替换为您自己的。
所以首先,我编写了一个简单的节点文件来创建一个 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
.
接下来我们创建另一个名为 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
,与 write-text 相反,delay
只是一个简单的基于承诺的延迟。
就是这样,使用..
const connect = require("path-to/connect");
const browser = await connect();
const page = await browser.newPage();
并且因为我们启动 Chromium 是分离的,当进程关闭/连接时,它会保持打开状态。我有大约 7 个进程与在 Chromium 中打开的 70 个网页相关联,没有任何问题。需要注意的一件事是,因为我确实在分离的 spawn 中启动了 chromium,所以如果你也需要的话,你可以手动关闭 chromium。另一种选择是在某些进程管理器中启动 chromiumLauncher.js
,例如 PM2 https://www.npmjs.com/package/pm2、
一般情况下,只要你的机器足够强大,同时打开两个浏览器是没有问题的。所以这个问题的答案完全取决于你机器的资源。您是否有足够的内存和 CPU 来支持多个打开的 Chrome 浏览器?
检查您是否有足够的资源
如果您正在使用 linux,请打开 htop 之类的工具来检查当任务 运行 时处理了多少内存和 CPU。当您达到 CPU/memory 极限时,您应该考虑 运行 按顺序执行任务(见下文)。
使用资源池
即使您有足够的资源,您也可以使用库 puppeteer-cluster(免责声明:我是作者)来处理并发处理。该库还将负责错误处理(如果浏览器崩溃怎么办?)并可以向您显示 运行.
期间的内存、CPU 使用情况和抓取统计信息代码示例
这是一个简单的示例,您可以如何使用它。
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);
});
})();
按顺序抓取
如果你的机器没有足够的资源来安装两个 运行ning 浏览器,你也可以 运行 一个接一个地执行任务,你只需要设置 [=11] 的值=] 到 1
。那么排队的任务将不会运行并行,而是顺序,因为只有一个打开的资源。