// 浏览器调试能力自检:发现端口 → 连 CDP → 列出标签页 → 读取页面信息 import fs from 'node:fs'; import path from 'node:path'; const CANDIDATES = [ 'C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\User Data\\DevToolsActivePort', 'C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\DevToolsActivePort', ]; const log = m => console.log(m); let lines = null, usedFile = null; for (const f of CANDIDATES) { if (fs.existsSync(f)) { const raw = fs.readFileSync(f, 'utf8').split('\n').map(s => s.trim()).filter(Boolean); if (raw.length >= 2) { lines = raw; usedFile = f; break; } } } if (!lines) { log('NOT_FOUND: 没找到 DevToolsActivePort,说明 Chrome 未以调试模式启动'); process.exit(0); } log('端口文件: ' + usedFile); log('端口: ' + lines[0] + ' 浏览器WS路径: ' + lines[1]); const url = `ws://127.0.0.1:${lines[0]}${lines[1]}`; const t0 = Date.now(); const ws = new WebSocket(url); const opened = await new Promise(res => { const timer = setTimeout(() => res(false), 60000); ws.addEventListener('open', () => { clearTimeout(timer); res(true); }, { once: true }); ws.addEventListener('error', () => { clearTimeout(timer); res(false); }, { once: true }); }); if (!opened) { log('FAIL: WebSocket 连不上(Chrome 调试端口可能已关闭或需重新开启 chrome://inspect)'); process.exit(0); } log(`CDP 握手成功,耗时 ${Date.now() - t0}ms`); let seq = 0; const pend = new Map(); ws.addEventListener('message', ev => { let m; try { m = JSON.parse(ev.data); } catch { return; } if (m.id && pend.has(m.id)) { const p = pend.get(m.id); pend.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } else if (m.method) events.push(m); }); const events = []; const send = (method, params = {}, sid) => new Promise((res, rej) => { const id = ++seq; pend.set(id, { res, rej }); const msg = { id, method, params }; if (sid) msg.sessionId = sid; ws.send(JSON.stringify(msg)); }); const { product, protocolVersion } = await send('Browser.getVersion'); log(`浏览器: ${product} (CDP 协议 v${protocolVersion})`); const { targetInfos } = await send('Target.getTargets'); const pages = targetInfos.filter(t => t.type === 'page'); log(`\n当前打开的标签页 ${pages.length} 个:`); pages.forEach((t, i) => log(` ${i + 1}. ${t.title.slice(0, 40)} | ${t.url.slice(0, 80)}`)); // 挑一个闲鱼页面做读取能力演示 const target = pages.find(p => /goofish/i.test(p.url)) || pages[0]; if (target) { const { sessionId } = await send('Target.attachToTarget', { targetId: target.targetId, flatten: true }); const r = await send('Runtime.evaluate', { expression: `({url: location.href, title: document.title, readyState: document.readyState, bodyLen: document.body ? document.body.innerText.length : 0, loginState: document.body ? (document.body.innerText.includes('登录') ? '页面上出现"登录"字样' : '未见登录字样') : 'n/a'})`, returnByValue: true, }, sessionId); log('\n读取能力验证(对目标页执行 JS 并取回结果):'); log(' ' + JSON.stringify(r.result.value, null, 2).replace(/\n/g, '\n ')); await send('Target.detachFromTarget', { sessionId }); } ws.close(); log('\n结论: 调试通道可用。'); setTimeout(() => process.exit(0), 300);