/** * Real Browser 启动:puppeteer-real-browser(抗 Cloudflare Turnstile) */ const { BASE_LAUNCH_ARGS, resolveChromeExecutable, } = require('./constants'); const { resolveProfileDir } = require('./profile-dir'); let realBrowserModuleChecked = null; let realBrowserAvailable = false; function isRealBrowserAvailable() { if (realBrowserModuleChecked !== null) return realBrowserAvailable; try { require.resolve('puppeteer-real-browser'); realBrowserAvailable = true; } catch (_) { realBrowserAvailable = false; } realBrowserModuleChecked = true; return realBrowserAvailable; } /** * @param {string[]} [extraArgs] * @param {{ sessionKey?: string, profile?: 'standard'|'real' }} [options] */ async function launchRealBrowser(extraArgs = [], options = {}) { if (!isRealBrowserAvailable()) { throw new Error( 'puppeteer-real-browser 未安装。请在 puppeteer-api 目录执行: npm install puppeteer-real-browser@1.4.4' ); } const { connect } = require('puppeteer-real-browser'); const chromePath = resolveChromeExecutable(); const profile = options.profile === 'real' ? 'real' : 'standard'; const profileDir = options.sessionKey ? resolveProfileDir(options.sessionKey, profile) : null; const args = [...BASE_LAUNCH_ARGS, '--mute-audio', ...extraArgs]; if (profileDir) { args.push(`--user-data-dir=${profileDir}`); } const result = await connect({ headless: false, turnstile: true, disableXvfb: false, customConfig: { chromePath }, args, }); const browser = result.browser; const page = result.page; if (!browser || !page) { throw new Error('puppeteer-real-browser connect 未返回 browser/page'); } browser.__persistentProfile = !!profileDir; browser.__userDataDir = profileDir || ''; return { browser, page }; } module.exports = { isRealBrowserAvailable, launchRealBrowser, };