Files
20260324-42433647/explore-pages.mjs
2026-04-25 21:50:03 +08:00

166 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 登录后探索 采购建议 和 仓库 页面,找导出按钮
*/
import { chromium } from 'playwright';
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
import { execSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const COOKIE_FILE = path.join(__dirname, 'cookies.json');
const SCREENSHOTS_DIR = path.join(__dirname, 'screenshots');
const OCR_SCRIPT = path.join(__dirname, 'ocr_captcha.py');
const BASE_URL = 'https://www.dianxiaomi.com';
mkdirSync(SCREENSHOTS_DIR, { recursive: true });
function ocrCaptcha(imagePath) {
try {
return execSync(`python3 "${OCR_SCRIPT}" "${imagePath}"`, { encoding: 'utf-8', timeout: 30000 }).trim() || null;
} catch { return null; }
}
async function login(browser) {
// 先试 Cookie
if (existsSync(COOKIE_FILE)) {
const ctx = await browser.newContext({
viewport: { width: 1920, height: 1080 }, locale: 'zh-CN',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
});
const pg = await ctx.newPage();
await ctx.addCookies(JSON.parse(readFileSync(COOKIE_FILE, 'utf-8')));
await pg.goto(`${BASE_URL}/home.htm`, { waitUntil: 'load', timeout: 20000 });
// 检查是否已登录(看页面有没有用户名)
const isLogged = await pg.evaluate(() => {
return document.body.innerText.includes('MiLe-kf01') || document.body.innerText.includes('待办事项');
});
if (isLogged) {
console.log('>> Cookie 有效');
return { page: pg, context: ctx };
}
await ctx.close();
}
// 登录
for (let i = 1; i <= 20; i++) {
console.log(`>> 登录尝试 ${i}...`);
const ctx = await browser.newContext({
viewport: { width: 1920, height: 1080 }, locale: 'zh-CN',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
});
const pg = await ctx.newPage();
let apiResult = null;
pg.on('response', async (r) => {
if (r.url().includes('userLoginNew2')) {
try { apiResult = await r.text(); } catch { apiResult = '__REDIRECT__'; }
}
});
try {
await pg.goto(`${BASE_URL}/home.htm`, { waitUntil: 'load', timeout: 30000 });
await pg.waitForSelector('#exampleInputName', { timeout: 10000 });
await pg.waitForFunction(() => typeof window.login === 'function', { timeout: 10000 });
await pg.waitForFunction(() => document.getElementById('verifyImgCode')?.complete, { timeout: 5000 }).catch(() => {});
await pg.waitForTimeout(1000);
const el = await pg.$('#verifyImgCode');
const capPath = path.join(SCREENSHOTS_DIR, 'cap.png');
await el.screenshot({ path: capPath });
const code = ocrCaptcha(capPath);
if (!code || code.length < 3) { await ctx.close(); continue; }
console.log(` 验证码: "${code}"`);
await pg.evaluate((c) => {
$('#exampleInputName').val('MiLe-kf01');
$('#exampleInputPassword').val('Vxdas@302');
$('#verifyCode').val(c);
}, code);
apiResult = null;
const nav = pg.waitForNavigation({ timeout: 15000, waitUntil: 'load' }).catch(() => null);
await pg.evaluate(() => { login(); });
await nav;
await pg.waitForTimeout(3000);
if (apiResult === '__REDIRECT__' || !pg.url().includes('/home.htm') ||
(apiResult && !apiResult.includes('"code":-1'))) {
console.log('>> 登录成功!');
const cookies = await ctx.cookies();
writeFileSync(COOKIE_FILE, JSON.stringify(cookies, null, 2));
return { page: pg, context: ctx };
}
console.log(` 失败: ${apiResult?.substring(0, 100)}`);
await ctx.close();
} catch (e) {
console.log(` 异常: ${e.message}`);
await ctx.close();
}
}
return null;
}
async function explorePage(pg, name, url) {
console.log(`\n>> ===== ${name} =====`);
try {
await pg.goto(url, { waitUntil: 'load', timeout: 20000 });
} catch {
console.log(' 加载超时');
return;
}
await pg.waitForTimeout(3000);
console.log(` URL: ${pg.url()}`);
console.log(` 标题: ${await pg.title()}`);
await pg.screenshot({ path: path.join(SCREENSHOTS_DIR, `explore-${name}.png`), fullPage: true });
// 查找所有按钮
const btns = await pg.$$eval('button, a, span, input[type="button"]', els =>
els.map(el => ({
tag: el.tagName, text: el.textContent.trim().substring(0, 50),
id: el.id, cls: (el.className || '').substring(0, 60),
onclick: el.getAttribute('onclick')?.substring(0, 80) || '',
href: el.href || '',
})).filter(e => e.text && e.text.length < 30)
.slice(0, 50)
);
console.log(` 按钮(${btns.length} 个):`);
for (const b of btns) {
const star = b.text.includes('导出') ? '★' : ' ';
console.log(` ${star} ${b.tag}#${b.id}: "${b.text}" onclick="${b.onclick}" ${b.href ? `href=${b.href}` : ''}`);
}
// 特别查找导出相关
const exportBtns = btns.filter(b => b.text.includes('导出') || b.text.includes('下载'));
if (exportBtns.length) {
console.log('\n ★★ 导出按钮详情:');
for (const b of exportBtns) {
console.log(` ${b.tag}#${b.id} class="${b.cls}" text="${b.text}" onclick="${b.onclick}" href="${b.href}"`);
}
}
// 页面内容关键部分
const content = await pg.evaluate(() => document.body?.innerText?.substring(0, 2000));
console.log(`\n 页面内容前1000字:\n ${content?.substring(0, 1000)}`);
}
// 主流程
const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
const result = await login(browser);
if (result) {
const { page } = result;
// 探索关键页面
await explorePage(page, '采购建议', `${BASE_URL}/purchasingProposal/index.htm?state=3`);
await explorePage(page, '采购单', `${BASE_URL}/dxmPurchasingNote/waitPayIndex.htm?state=2`);
await explorePage(page, '仓库商品', `${BASE_URL}/warehouseProduct/index.htm`);
await explorePage(page, '自定导出', `${BASE_URL}/sys/index.htm?go=m409`);
await result.context.close();
}
await browser.close();