Files
links/patches/puppeteer-api/lib/browser-factory.js
T

151 lines
4.7 KiB
JavaScript
Raw Normal View History

2026-06-20 04:47:34 +08:00
/**
* BrowserFactorystandard / real 双轨启动 + 温池 + 独立并发限流
2026-06-20 04:47:34 +08:00
*/
const {
DEFAULT_UA,
MAX_CONCURRENT_BROWSERS,
MAX_CONCURRENT_BROWSERS_REAL,
QUEUE_TIMEOUT_MS,
NAVIGATION_TIMEOUT_MS,
PAGE_DEFAULT_TIMEOUT_MS,
2026-06-20 04:47:34 +08:00
} = require('./constants');
const { BrowserConcurrencyLimiter } = require('./concurrency');
const { launchStandardBrowser } = require('./launch-standard');
const { launchRealBrowser, isRealBrowserAvailable } = require('./launch-real-browser');
const { browserPool } = require('./browser-pool');
2026-06-20 04:47:34 +08:00
const standardLimiter = new BrowserConcurrencyLimiter(MAX_CONCURRENT_BROWSERS);
const realLimiter = new BrowserConcurrencyLimiter(MAX_CONCURRENT_BROWSERS_REAL);
function normalizeAntiBot(antiBot) {
if (!antiBot || !antiBot.enabled) {
return { enabled: false, profile: 'standard' };
}
return {
enabled: true,
profile: antiBot.profile === 'real' ? 'real' : 'standard',
turnstile: antiBot.turnstile !== false,
solverFallback: antiBot.solverFallback !== false,
sessionKey: antiBot.sessionKey || '',
challengeTimeoutMs: antiBot.challengeTimeoutMs || 60000,
2026-06-29 04:54:41 +08:00
postCfReadyUrlContains: antiBot.postCfReadyUrlContains || '',
postCfReadySelector: antiBot.postCfReadySelector || '',
postCfReadyTimeoutMs: antiBot.postCfReadyTimeoutMs || 0,
postCfSettleMs: antiBot.postCfSettleMs || 0,
2026-06-20 04:47:34 +08:00
};
}
function getLimiter(profile) {
return profile === 'real' ? realLimiter : standardLimiter;
}
/**
* 冷启动 Browser(供温池 launcher 使用)
* @param {{ profile?: 'standard'|'real', extraArgs?: string[], sessionKey?: string }} options
2026-06-20 04:47:34 +08:00
*/
async function createBrowserSession(options = {}) {
const profile = options.profile === 'real' ? 'real' : 'standard';
const extraArgs = options.extraArgs || [];
const sessionKey = options.sessionKey || '';
2026-06-20 04:47:34 +08:00
if (profile === 'real') {
const { browser, page } = await launchRealBrowser(extraArgs, { sessionKey, profile: 'real' });
2026-06-20 04:47:34 +08:00
return {
browser,
page,
profile: 'real',
cleanup: async (destroy = false) => {
if (destroy || !sessionKey) {
try {
await browser.close();
} catch (_) { /* ignore */ }
2026-06-20 04:47:34 +08:00
}
},
};
}
const browser = await launchStandardBrowser(extraArgs, { sessionKey, profile: 'standard' });
2026-06-20 04:47:34 +08:00
return {
browser,
page: null,
profile: 'standard',
cleanup: async (destroy = false) => {
if (destroy || !sessionKey) {
try {
await browser.close();
} catch (_) { /* ignore */ }
2026-06-20 04:47:34 +08:00
}
},
};
}
/**
* 从温池或冷启动获取 Browser
* @param {{ profile?: 'standard'|'real', extraArgs?: string[], sessionKey?: string }} options
2026-06-20 04:47:34 +08:00
*/
async function acquireBrowserSession(options = {}) {
const profile = options.profile === 'real' ? 'real' : 'standard';
const sessionKey = options.sessionKey || '';
const extraArgs = options.extraArgs || [];
return browserPool.acquire(sessionKey, profile, async () => createBrowserSession({
profile,
extraArgs,
sessionKey,
}));
}
2026-06-20 04:47:34 +08:00
async function runWithProfileLimit(taskName, profile, fn) {
const limiter = getLimiter(profile);
try {
return await limiter.run(taskName, fn);
} catch (err) {
if (err.message === 'QUEUE_TIMEOUT') err.code = 'QUEUE_TIMEOUT';
throw err;
}
}
async function createManagedPage(browser, existingPage, options = {}) {
const page = existingPage || await browser.newPage();
page.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT_MS);
page.setDefaultTimeout(PAGE_DEFAULT_TIMEOUT_MS);
2026-06-20 04:47:34 +08:00
2026-06-29 04:54:41 +08:00
if (!options.skipUserAgent) {
const userAgent = options.userAgent || DEFAULT_UA;
await page.setUserAgent(userAgent);
}
2026-06-20 04:47:34 +08:00
if (options.viewport) await page.setViewport(options.viewport);
2026-06-20 04:47:34 +08:00
if (options.cookies && options.cookies.length > 0) {
await page.setCookie(...options.cookies);
}
page.on('error', (err) => console.error('[Page Error]', err.message));
page.on('pageerror', (err) => console.error('[Page JS Error]', err.message));
return page;
}
function getFactoryStats() {
return {
standard: standardLimiter.getStats(),
real: realLimiter.getStats(),
realBrowserReady: isRealBrowserAvailable(),
pool: browserPool.getStats(),
2026-06-20 04:47:34 +08:00
};
}
module.exports = {
normalizeAntiBot,
getLimiter,
createBrowserSession,
acquireBrowserSession,
2026-06-20 04:47:34 +08:00
runWithProfileLimit,
createManagedPage,
getFactoryStats,
standardLimiter,
realLimiter,
browserPool,
2026-06-20 04:47:34 +08:00
QUEUE_TIMEOUT_MS,
};