/** * Real Browser 启动:puppeteer-real-browser(抗 Cloudflare Turnstile) * 注意:不与 puppeteer-extra 混用在同一 Browser 实例 */ const { BASE_LAUNCH_ARGS, resolveChromeExecutable, } = require('./constants'); /** @type {boolean|null} */ let realBrowserModuleChecked = null; /** @type {boolean} */ let realBrowserAvailable = false; /** * 检测 puppeteer-real-browser 是否已安装 * @returns {boolean} */ function isRealBrowserAvailable() { if (realBrowserModuleChecked !== null) { return realBrowserAvailable; } try { require.resolve('puppeteer-real-browser'); realBrowserAvailable = true; } catch (_) { realBrowserAvailable = false; } realBrowserModuleChecked = true; return realBrowserAvailable; } /** * 启动 real profile Browser(headless:false + Xvfb + turnstile 内置点击) * @param {string[]} [extraArgs] * @returns {Promise<{ browser: import('puppeteer').Browser, page: import('puppeteer').Page }>} */ async function launchRealBrowser(extraArgs = []) { 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 result = await connect({ headless: false, turnstile: true, disableXvfb: false, customConfig: { chromePath }, args: [...BASE_LAUNCH_ARGS, '--mute-audio', ...extraArgs], }); const browser = result.browser; const page = result.page; if (!browser || !page) { throw new Error('puppeteer-real-browser connect 未返回 browser/page'); } return { browser, page }; } module.exports = { isRealBrowserAvailable, launchRealBrowser, };