ERPTurbo_Poster/lib/browser.js
2025-11-14 14:18:32 +08:00

215 lines
5.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import puppeteer from 'puppeteer';
import {existsSync} from 'fs';
const browserArgs = [
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-setuid-sandbox',
'--no-first-run',
'--no-sandbox',
'--no-zygote',
// '--single-process',
'--disable-background-networking',
'--enable-features=NetworkService,NetworkServiceInProcess',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-breakpad',
'--disable-client-side-phishing-detection',
'--disable-component-extensions-with-background-pages',
'--disable-default-apps',
'--disable-extensions',
'--disable-features=TranslateUI',
'--disable-hang-monitor',
'--disable-ipc-flooding-protection',
'--disable-popup-blocking',
'--disable-prompt-on-repost',
'--disable-renderer-backgrounding',
'--disable-sync',
'--force-color-profile=srgb',
'--metrics-recording-only',
'--no-first-run',
'--enable-automation',
'--password-store=basic',
'--use-mock-keychain',
];
// 尝试查找系统中已安装的 Chrome 或 Chromium 可执行文件路径
function getChromeExecutablePath() {
// 检查环境变量中是否指定了 Chromium 路径
if (process.env.PUPPETEER_EXECUTABLE_PATH) {
return process.env.PUPPETEER_EXECUTABLE_PATH;
}
const paths = [
// Windows
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
// macOS
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
// Linux
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser',
'/usr/bin/chromium'
];
for (const path of paths) {
if (existsSync(path)) {
return path;
}
}
return null;
}
class BrowserManager {
constructor() {
this.browserList = [];
this.unusedBrowser = [];
this.pages = [];
}
async createBrowser(max_wse) {
// 尝试使用系统已安装的 Chrome/Chromium
const executablePath = getChromeExecutablePath();
const launchOptions = {
headless: false, // 默认使用无头模式
args: browserArgs
};
// 如果找到了系统 Chrome则使用它
if (executablePath) {
console.log('Using system Chrome:', executablePath);
launchOptions.executablePath = executablePath;
} else {
console.log('Using bundled Chromium');
}
const browser = await puppeteer.launch(launchOptions);
let pageCount = 0;
// 监听到打开了一个新窗口
browser.on("targetcreated", (data) => {
pageCount++;
});
browser.on("targetdestroyed", () => {
pageCount--;
if (pageCount === 0 && this.unusedBrowser.includes(browser.wsEndpoint())) {
console.log("命中unusedBrowser");
this.delBrowser(browser.wsEndpoint());
}
});
// max_wse
await browser.newPage();
await browser.newPage();
await browser.newPage();
const pages = await browser.pages();
for (const page of pages) {
this.pages.push(page);
}
this.browserList.push(browser.wsEndpoint());
return browser;
}
async initBrowser(max_wse) {
try {
await this.createBrowser(max_wse).then(() => {
console.log("初始化浏览器成功");
});
} catch (error) {
console.error("初始化浏览器失败:", error);
throw error;
}
// 每隔24小时去创建一个新的Browser实例并销毁第一个实例
setInterval(() => {
console.log(this.browserList.join(','));
console.log(this.unusedBrowser.join(','));
this.createBrowser().then(() => {
// 把第一个浏览器置为不可用
this.getFirstBrowser().then(browser => {
browser.pages().then((pages) => {
console.log(`第一个浏览器实例打开的页面数为${pages.length}`);
// 如果当前没有打开的页面创建Browser实例时会默认初始化一个页面所以这里判断的是<=1
if (pages.length <= 1) {
this.delBrowser(browser.wsEndpoint()).then(() => console.log("关闭浏览器成功"));
} else {
this.unusedBrowser.push(browser.wsEndpoint());
}
});
});
});
}, 24 * 60 * 60 * 1000);
}
/**
* 获取页面
*/
getTargetPage() {
while (true) {
const page = this.pages.pop();
if (page !== undefined) {
// 检查页面是否仍然有效
if (!page.isClosed()) {
return page;
}
// 如果页面已关闭,则丢弃并继续获取下一个
console.log('发现已关闭的页面,已丢弃');
} else {
// 如果没有可用页面,则创建新页面
console.log('没有可用页面,需要创建新页面');
// 这里应该触发创建新浏览器实例的逻辑
return null;
}
}
}
/**
* 归还页面
*/
returnTargetPage(target) {
// 只有当页面未关闭时才归还
if (target && !target.isClosed()) {
this.pages.push(target);
}
}
/**
* 从browserList和unusedBrowser中删除
* @param browser
*/
async delBrowser(browser) {
this.browserList = this.browserList.filter((item) => item !== browser);
this.unusedBrowser = this.unusedBrowser.filter((item) => item !== browser);
const browserWSEndpoint = browser;
const browserInstance = await puppeteer.connect({browserWSEndpoint});
await browserInstance.close();
console.log("删除浏览器 %o %o", this.browserList.length, this.unusedBrowser.length);
}
/**
* 始终返回最后一个浏览器实例
*/
async getBrowser() {
const browserWSEndpoint = this.browserList[this.browserList.length - 1];
return puppeteer.connect({browserWSEndpoint});
}
/**
* 始终返回第一个浏览器实例
*/
async getFirstBrowser() {
const browserWSEndpoint = this.browserList[0];
return puppeteer.connect({browserWSEndpoint});
}
}
export default BrowserManager;