first commit
This commit is contained in:
commit
9399a88d39
347 files changed
+167716
No files matched your search
@@ -0,0 +1,101 @@
|
||||
// 只读:买家侧搜索竞争侦察(隐藏标签页,跑完关闭)
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const KWS = fs.readFileSync('.cdp/keywords.txt', 'utf8')
|
||||
.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
|
||||
const OUT = path.resolve('.cdp/out/search');
|
||||
fs.mkdirSync(OUT, { recursive: true });
|
||||
|
||||
const UDD = '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);
|
||||
const say = s => console.log(String(s));
|
||||
|
||||
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 timeout')), 15000);
|
||||
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.reject(new Error(m.error.message)) : p.resolve(m.result);
|
||||
} else if (m.method) c.onEvent?.(m.method, m.params, m.sessionId);
|
||||
});
|
||||
return c;
|
||||
}
|
||||
send(method, params = {}, sid, t = 90000) {
|
||||
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, { resolve: res, reject: rej, timer });
|
||||
const msg = { id, method, params }; if (sid) msg.sessionId = sid;
|
||||
this.ws.send(JSON.stringify(msg));
|
||||
});
|
||||
}
|
||||
close() { try { this.ws.close(); } catch {} }
|
||||
}
|
||||
|
||||
const cdp = await Cdp.attach(`ws://127.0.0.1:${lines[0]}${lines[1]}`);
|
||||
const first = 'https://www.goofish.com/search?q=' + encodeURIComponent(KWS[0]);
|
||||
const { targetId } = await cdp.send('Target.createTarget', { url: first, background: true });
|
||||
const { sessionId: S } = await cdp.send('Target.attachToTarget', { targetId, flatten: true });
|
||||
await cdp.send('Page.enable', {}, S);
|
||||
say('tab created for ' + KWS.length + ' 词');
|
||||
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
let last = Date.now();
|
||||
cdp.onEvent = (m, p, sid) => { if (sid === S) last = Date.now(); };
|
||||
|
||||
const EXTRACT = `(() => {
|
||||
const t = e => (e.innerText || '').replace(/\\s+/g,' ').trim();
|
||||
const links = Array.from(document.querySelectorAll('a[href*="item?id="], a[href*="/item"]'));
|
||||
const cards = [];
|
||||
const seen = new Set();
|
||||
for (const a of links) {
|
||||
const id = (a.href.match(/id=(\\d+)/) || [])[1];
|
||||
if (!id || seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
// 向上找卡片容器,拿完整文本
|
||||
let node = a, txt = t(a);
|
||||
for (let i = 0; i < 5 && node.parentElement; i++) {
|
||||
node = node.parentElement;
|
||||
const nt = t(node);
|
||||
if (nt.length > txt.length && nt.length < 600) txt = nt;
|
||||
if (nt.length >= 600) break;
|
||||
}
|
||||
const img = a.querySelector('img') || node.querySelector('img');
|
||||
const title = (img && (img.alt || '')) || '';
|
||||
const price = (txt.match(/¥\\s*[\\d.]+/) || [''])[0];
|
||||
const want = (txt.match(/([\\d.]+万?)\\s*人想要/) || ['',''])[1];
|
||||
cards.push({ id, title: title.slice(0,90), price, want, text: txt.slice(0, 200) });
|
||||
}
|
||||
const total = (document.body.innerText.match(/([\\d,]+)\\s*(件|个)宝贝/) || [])[1] || null;
|
||||
return { href: location.href, title: document.title, count: cards.length, total, cards: cards.slice(0, 40) };
|
||||
})()`;
|
||||
|
||||
const results = {};
|
||||
for (const kw of KWS) {
|
||||
const url = 'https://www.goofish.com/search?q=' + encodeURIComponent(kw);
|
||||
await cdp.send('Page.navigate', { url }, S);
|
||||
last = Date.now();
|
||||
const t0 = Date.now();
|
||||
while (Date.now() - t0 < 25000) { await sleep(400); if (Date.now() - last > 4000 && Date.now() - t0 > 6000) break; }
|
||||
const r = await cdp.send('Runtime.evaluate', { expression: EXTRACT, returnByValue: true, awaitPromise: true }, S, 60000);
|
||||
const v = r.exceptionDetails ? { __error: r.exceptionDetails.text } : r.result?.value;
|
||||
results[kw] = v;
|
||||
fs.writeFileSync(path.join(OUT, kw.replace(/[^\w\u4e00-\u9fa5]/g, '_') + '.json'), JSON.stringify(v, null, 2), 'utf8');
|
||||
say(`${kw} -> count=${v && v.count} total=${v && v.total}`);
|
||||
}
|
||||
|
||||
await cdp.send('Target.closeTarget', { targetId });
|
||||
say('tab closed; saved to ' + OUT);
|
||||
cdp.close();
|
||||
process.exit(0);
|
||||
Reference in new issue
Block a user