/** * 第一步:有头浏览器手动登录,保存 Cookie * 用法:node login-save-cookies.mjs * * 运行后会打开浏览器,手动输入验证码并登录, * 登录成功后脚本自动保存 Cookie 到 cookies.json */ import { chromium } from 'playwright'; import { writeFileSync, existsSync } from 'fs'; const COOKIE_FILE = './cookies.json'; const browser = await chromium.launch({ headless: false, // 有头模式让你操作 channel: 'chrome', // 用系统 Chrome }); const context = await browser.newContext({ viewport: { width: 1920, height: 1080 }, locale: 'zh-CN', }); const page = await context.newPage(); console.log('>> 打开店小秘登录页...'); console.log('>> 请在浏览器中手动输入验证码并登录'); console.log('>> 登录成功后脚本会自动保存 Cookie\n'); await page.goto('https://www.dianxiaomi.com/home.htm', { waitUntil: 'networkidle', timeout: 30000 }); // 自动填入账号密码 const usernameInput = await page.$('input[name="account"]'); const passwordInput = await page.$('input[type="password"]'); const rememberCheckbox = await page.$('input[name="remeber"]'); if (usernameInput && passwordInput) { await usernameInput.fill('MiLe-kf01'); await passwordInput.fill('Vxdas@302'); // 勾选"记住我"延长 Cookie 有效期 if (rememberCheckbox) { await rememberCheckbox.check(); } console.log('>> 已自动填入账号密码,请手动输入验证码后点击"登录"'); } // 等待用户手动登录成功 - 检测 URL 变化(离开登录页) // 店小秘后台通常会跳转到 /saleManage/ 或类似路径 try { await page.waitForURL(url => { const u = url.toString(); return !u.includes('/home.htm') && !u.includes('/index.htm') && u.includes('dianxiaomi.com'); }, { timeout: 300000 }); // 等待 5 分钟 console.log('\n>> 登录成功!当前URL:', page.url()); // 多等几秒让页面完全加载 await page.waitForTimeout(3000); // 保存 Cookie const cookies = await context.cookies(); writeFileSync(COOKIE_FILE, JSON.stringify(cookies, null, 2)); console.log(`>> Cookie 已保存到 ${COOKIE_FILE}(共 ${cookies.length} 条)`); // 截图后台页面 await page.screenshot({ path: './screenshots/04-dashboard.png', fullPage: true }); console.log('>> 已截图: screenshots/04-dashboard.png'); // 打印页面结构 - 查找导航菜单 console.log('\n>> ===== 后台菜单结构 ====='); // 通常的左侧菜单 const menuItems = await page.$$eval( 'a, .menu-item, [class*="menu"] a, [class*="nav"] a, .sidebar a, li a', els => els .map(el => ({ text: el.textContent.trim(), href: el.href, className: el.className })) .filter(e => e.text && e.text.length < 40 && e.text.length > 0) .filter((e, i, arr) => arr.findIndex(a => a.text === e.text) === i) // 去重 .slice(0, 80) ); for (const item of menuItems) { console.log(` [${item.text}] -> ${item.href}`); } // 特别搜索采购、仓库、导出相关 console.log('\n>> ===== 采购/仓库/导出 相关菜单 ====='); const relatedItems = menuItems.filter(i => i.text.includes('采购') || i.text.includes('仓库') || i.text.includes('导出') || i.text.includes('库存') || i.text.includes('备货') ); for (const item of relatedItems) { console.log(` ★ [${item.text}] -> ${item.href}`); } // 保持浏览器打开让用户可以继续观察 console.log('\n>> 浏览器将在 60 秒后关闭(你可以在这段时间内浏览后台页面)'); await page.waitForTimeout(60000); } catch (e) { if (e.name === 'TimeoutError') { console.log('\n>> 等待超时(5分钟),请重新运行脚本'); } else { console.error('错误:', e.message); } // 检查是否已经在后台了(可能 URL 检测没覆盖到) const currentUrl = page.url(); console.log('>> 当前 URL:', currentUrl); if (currentUrl.includes('dianxiaomi.com') && !currentUrl.includes('/home.htm')) { const cookies = await context.cookies(); writeFileSync(COOKIE_FILE, JSON.stringify(cookies, null, 2)); console.log(`>> 看起来已登录,Cookie 已保存到 ${COOKIE_FILE}`); } await page.waitForTimeout(30000); } await browser.close(); console.log('>> 完成');