Files
ObsidianHome/小鱼/.cdp/cdp-overview.mjs
T

96 lines
4.5 KiB
JavaScript

// 只读:数据总览 seller.summary 逐日序列(近1/7/30天),按 realDateRange 裁剪
import fs from 'node:fs';
import path from 'node:path';
const OUT = path.resolve('.cdp/out');
fs.mkdirSync(OUT, { recursive: true });
const UDD = process.env.CHROME_USER_DATA_DIR || 'C:\\Users\\Administrator\\AppData\\Local\\Google\\Chrome\\User Data';
const lines = fs.readFileSync(path.join(UDD, 'DevToolsActivePort'), 'utf8').split('\n').map(s => s.trim()).filter(Boolean);
class Cdp {
constructor(ws) { this.ws = ws; this.seq = 0; this.pending = new Map(); }
static async attach(url) {
const ws = new WebSocket(url);
await new Promise((res, rej) => {
const t = setTimeout(() => rej(new Error('ws open timeout')), 60000);
ws.addEventListener('open', () => { clearTimeout(t); res(); }, { once: true });
ws.addEventListener('error', () => { clearTimeout(t); rej(new Error('ws error')); }, { once: true });
});
const c = new Cdp(ws);
ws.addEventListener('message', ev => {
let m; try { m = JSON.parse(ev.data); } catch { return; }
if (m.id && c.pending.has(m.id)) { const p = c.pending.get(m.id); c.pending.delete(m.id); clearTimeout(p.timer); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); }
});
return c;
}
send(method, params = {}, sid, t = 120000) {
const id = ++this.seq;
return new Promise((res, rej) => {
const timer = setTimeout(() => { this.pending.delete(id); rej(new Error('timeout ' + method)); }, t);
this.pending.set(id, { res, rej, timer });
const msg = { id, method, params }; if (sid) msg.sessionId = sid;
this.ws.send(JSON.stringify(msg));
});
}
async ev(expression, sid) {
const r = await this.send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true }, sid);
if (r.exceptionDetails) return { __error: r.exceptionDetails.text + ' :: ' + (r.exceptionDetails.exception?.description || '') };
return r.result?.value;
}
close() { try { this.ws.close(); } catch {} }
}
const cdp = await Cdp.attach(`ws://127.0.0.1:${lines[0]}${lines[1]}`);
// 找已有闲鱼标签页,没有则新建
const { targetInfos } = await cdp.send('Target.getTargets');
let target = targetInfos.find(t => t.type === 'page' && /seller\.goofish\.com/.test(t.url));
let created = false;
if (!target) {
const { targetId } = await cdp.send('Target.createTarget', { url: 'https://seller.goofish.com/?site=COMMONPRO#/seller-data/data', background: true });
created = true;
target = { targetId };
}
const { sessionId: S } = await cdp.send('Target.attachToTarget', { targetId: target.targetId, flatten: true });
await cdp.send('Page.enable', {}, S);
await new Promise(r => setTimeout(r, 6000));
const EXPR = `(async () => {
const api = 'mtop.alibaba.idle.seller.pc.datacompass.seller.summary';
const H = { idle_site_biz_code: 'COMMONPRO', idle_user_group_member_id: '' };
const call = (dateType) => lib.mtop.request({
api, v: '1.0', type: 'POST', dataType: 'json', ext_headers: H,
data: { dateType, dateRange: '', ms: '', tabType: 'all' },
});
const out = { err: null, data: {} };
for (const dt of ['recent1d','recent7d','recent30d']) {
try {
const r = await call(dt);
const d = r && r.data && r.data.data;
if (!d) { out.data[dt] = { ret: r && r.ret, empty: true }; continue; }
out.data[dt] = {
realDateRange: d.extendInfo && d.extendInfo.realDateRange || null,
summaryKeys: Object.keys(d),
graph: d.graphDataList || null,
others: Object.fromEntries(Object.entries(d).filter(([k]) => k !== 'graphDataList')),
};
} catch (e) { out.data[dt] = { err: (e && e.data && e.data.errorMsg) || String(e && e.message || e) }; }
}
return out;
})()`;
const res = await cdp.ev(EXPR, S);
fs.writeFileSync(path.join(OUT, 'overview-raw.json'), JSON.stringify(res, null, 2), 'utf8');
console.log('saved overview-raw.json');
console.log('keys=' + JSON.stringify(res && res.data ? Object.keys(res.data) : null));
if (res && res.err) console.log('err=' + res.err);
for (const dt of ['recent1d','recent7d','recent30d']) {
const d = res?.data?.[dt];
if (!d) { console.log(dt + ': none'); continue; }
console.log(dt + ': realDateRange=' + JSON.stringify(d.realDateRange) + ' graphLen=' + (Array.isArray(d.graph) ? d.graph.length : 'n/a') + (d.err ? ' ERR=' + d.err : ''));
}
if (created) await cdp.send('Target.closeTarget', { targetId: target.targetId });
await cdp.send('Target.detachFromTarget', { sessionId: S }).catch(() => {});
cdp.close();
setTimeout(() => process.exit(0), 300);