diff --git a/lib/lmarena.js b/lib/lmarena.js index d1bd23e..ede3024 100644 --- a/lib/lmarena.js +++ b/lib/lmarena.js @@ -2,6 +2,7 @@ import puppeteer from 'puppeteer'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; +import { anonymizeProxy, closeAnonymizedProxy } from 'proxy-chain'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -285,13 +286,32 @@ async function initBrowser(config) { } // 4. 代理配置 + let proxyUrlForChrome = null; if (chromeConfig.proxy && chromeConfig.proxy.enable) { - const { type, host, port } = chromeConfig.proxy; - const proxyUrl = type === 'socks5' ? `socks5://${host}:${port}` : `${host}:${port}`; - args.push(`--proxy-server=${proxyUrl}`); - // 禁用 QUIC (HTTP3) 以确保代理兼容性 - args.push('--disable-quic'); - console.log(`>>> [Browser] 代理配置: ${type}://${host}:${port}`); + const { type, host, port, user, passwd } = chromeConfig.proxy; + + // 特殊处理 SOCKS5 + Auth (Chrome 原生不支持) + if (type === 'socks5' && user && passwd) { + try { + const upstreamUrl = `socks5://${user}:${passwd}@${host}:${port}`; + console.log(`>>> [Browser] 检测到 SOCKS5 认证代理,正在创建本地桥接...`); + // 创建本地中间代理 (无认证 -> 有认证) + proxyUrlForChrome = await anonymizeProxy(upstreamUrl); + console.log(`>>> [Browser] 本地桥接已建立: ${proxyUrlForChrome} -> ${host}:${port}`); + + args.push(`--proxy-server=${proxyUrlForChrome}`); + args.push('--disable-quic'); + } catch (e) { + console.error('>>> [Error] 代理桥接创建失败:', e); + throw e; + } + } else { + // 常规 HTTP 代理或无认证 SOCKS5 + const proxyUrl = type === 'socks5' ? `socks5://${host}:${port}` : `${host}:${port}`; + args.push(`--proxy-server=${proxyUrl}`); + args.push('--disable-quic'); + console.log(`>>> [Browser] 代理配置: ${type}://${host}:${port}`); + } } const browser = await puppeteer.launch({ @@ -307,13 +327,13 @@ async function initBrowser(config) { const pages = await browser.pages(); const page = pages[0]; - // 5. 代理认证 - if (chromeConfig.proxy && chromeConfig.proxy.enable && chromeConfig.proxy.user) { + // 5. 代理认证 (仅当未使用 proxy-chain 桥接时) + if (chromeConfig.proxy && chromeConfig.proxy.enable && chromeConfig.proxy.user && !proxyUrlForChrome) { await page.authenticate({ username: chromeConfig.proxy.user, password: chromeConfig.proxy.passwd }); - console.log('>>> [Browser] 代理认证: 已设置'); + console.log('>>> [Browser] 代理认证: 已设置 (HTTP Basic Auth)'); } // 隐藏 WebDriver 特征 @@ -359,6 +379,18 @@ async function initBrowser(config) { console.log('>>> [Browser] 浏览器初始化完成,系统就绪'); + // 注册清理钩子:浏览器关闭时关闭代理服务器 + if (proxyUrlForChrome) { + browser.on('disconnected', async () => { + console.log('>>> [Browser] 浏览器断开,正在清理代理桥接...'); + try { + await closeAnonymizedProxy(proxyUrlForChrome, true); + } catch (e) { + console.error('>>> [Warn] 代理清理失败:', e.message); + } + }); + } + return { browser, page, client, width, height }; } diff --git a/package.json b/package.json index 4814efd..3492d90 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dependencies": { "got-scraping": "^4.1.2", "js-yaml": "^4.1.1", + "proxy-chain": "^2.6.0", "puppeteer": "^24.31.0", "sharp": "^0.34.5" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b5b868..0a84a0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: js-yaml: specifier: ^4.1.1 version: 4.1.1 + proxy-chain: + specifier: ^2.6.0 + version: 2.6.0 puppeteer: specifier: ^24.31.0 version: 24.31.0 @@ -610,6 +613,10 @@ packages: resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} engines: {node: '>= 14'} + proxy-chain@2.6.0: + resolution: {integrity: sha512-+NpVKSk68j8sQJG2tBbFuJxMzKTlqeCXXFbqvlyiFhnmxdcYJSv4XZzUSIfwIUwR3D0T8fEJqrA4C7yykU40Pw==} + engines: {node: '>=14'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -1300,6 +1307,14 @@ snapshots: transitivePeerDependencies: - supports-color + proxy-chain@2.6.0: + dependencies: + socks: 2.8.7 + socks-proxy-agent: 8.0.5 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + proxy-from-env@1.1.0: {} pump@3.0.3: