UNPKG

undetected-puppeteer

Version:

1:1 (sorta) replacement for Puppeteer, but undetected

154 lines (132 loc) 5.48 kB
const puppeteer = require('puppeteer'); const { execFile } = require('child_process'); const axios = require('axios'); const fs = require('fs'); const path = require('path'); const os = require('os'); const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function launch(options = {}) { const { headless = false, args = [], defaultViewport = { width: 800, height: 600 }, executablePath, userDataDir, ...otherOptions } = options; const tempUserDataDir = !userDataDir ? fs.mkdtempSync( path.join(os.tmpdir(), 'puppeteer_dev_chrome_profile-') ) : null; try { const debugPort = options.port || Math.floor(Math.random() * (9999 - 9000) + 9000); // trying to replicate puppeteer chrome launch arguments is hell const chromeArgs = [ `--remote-debugging-port=${debugPort}`, `--user-data-dir=${userDataDir || tempUserDataDir}`, '--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-dev-shm-usage', '--disable-extensions', '--disable-features=Translate', '--disable-hang-monitor', '--disable-ipc-flooding-protection', '--disable-popup-blocking', '--disable-prompt-on-repost', '--disable-renderer-backgrounding', '--disable-sync', '--metrics-recording-only', '--no-first-run', '--no-default-browser-check', '--password-store=basic', '--use-mock-keychain', '--allow-pre-commit-input', '--disable-domain-reliability', '--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process', '--disable-component-update', '--no-service-autorun', // idk if ts needed maybe delete? '--disable-blink-features=AutomationControlled' ]; const userArgs = args.filter(arg => !arg.startsWith('--remote-debugging-port=') && !arg.startsWith('--user-data-dir=')); chromeArgs.push(...userArgs); if (headless) { chromeArgs.push('--headless=new'); if (!args.some(arg => arg.startsWith('--user-agent='))) { chromeArgs.push(`--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`); } if (!args.some(arg => arg.includes('--sandbox'))) { chromeArgs.push('--no-sandbox', '--disable-setuid-sandbox'); } } if (!args.some(arg => arg.startsWith('--window-size')) && defaultViewport) { chromeArgs.push(`--window-size=${defaultViewport.width},${defaultViewport.height}`); } const chromeProcess = execFile((executablePath || puppeteer.executablePath()), chromeArgs); let webSocketUrl; let attempts = 0; const maxAttempts = 15; while (!webSocketUrl && attempts < maxAttempts) { try { await delay(500); const response = await axios.get(`http://127.0.0.1:${debugPort}/json/version`, { timeout: 1000 }); webSocketUrl = response.data.webSocketDebuggerUrl; } catch { attempts++; } } if (!webSocketUrl) { throw new Error(`Failed to connect to Chrome on port ${debugPort}`); } const browser = await puppeteer.connect({ browserWSEndpoint: webSocketUrl, defaultViewport, ...otherOptions }); const cleanup = async (fromClose = false) => { try { if (!fromClose) { try { await browser.disconnect(); } catch {} } chromeProcess.kill('SIGKILL'); if (tempUserDataDir) { fs.rm(tempUserDataDir, { recursive: true, force: true }, () => {}); } } catch {} }; const old = browser.close.bind(browser); browser.close = async () => { await old(); await cleanup(true); }; browser.on('disconnected', () => cleanup()); process.once('exit', () => cleanup()); process.once('SIGINT', () => { cleanup(); process.exit(130); }); process.once('SIGTERM', () => { cleanup(); process.exit(143); }); return browser; } catch (error) { if (tempUserDataDir) { fs.rm(tempUserDataDir, { recursive: true, force: true }, () => {}); } throw error; } } module.exports = { ...puppeteer, launch };