120 lines
4.2 KiB
JavaScript
120 lines
4.2 KiB
JavaScript
/**
|
||
* 第一步:有头浏览器手动登录,保存 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('>> 完成');
|