reviews.js
1 import { cli, Strategy } from '@jackwener/opencli/registry'; 2 import { clampInt, normalizeNumericId } from '../_shared/common.js'; 3 cli({ 4 site: 'jd', 5 name: 'reviews', 6 description: '京东商品评价', 7 domain: 'item.jd.com', 8 strategy: Strategy.COOKIE, 9 args: [ 10 { name: 'sku', positional: true, required: true, help: '商品 SKU ID' }, 11 { name: 'limit', type: 'int', default: 10, help: '返回评价数量 (max 20)' }, 12 ], 13 columns: ['rank', 'user', 'content', 'date'], 14 navigateBefore: false, 15 func: async (page, kwargs) => { 16 const sku = normalizeNumericId(kwargs.sku, 'sku', '100291143898'); 17 const limit = clampInt(kwargs.limit, 10, 1, 20); 18 await page.goto(`https://item.jd.com/${sku}.html`); 19 await page.wait(5); 20 await page.autoScroll({ times: 2, delayMs: 1500 }); 21 const data = await page.evaluate(` 22 (async () => { 23 const text = document.body?.innerText || ''; 24 const reviewStart = text.indexOf('买家评价'); 25 const reviewEnd = text.indexOf('全部评价'); 26 if (reviewStart < 0) return []; 27 28 const reviewSection = text.substring(reviewStart, reviewEnd > reviewStart ? reviewEnd : reviewStart + 3000); 29 const lines = reviewSection.split('\\n').map(l => l.trim()).filter(Boolean); 30 31 const results = []; 32 const userPattern = /^[a-zA-Z0-9*_]{3,15}$/; 33 for (let i = 0; i < lines.length; i++) { 34 const line = lines[i]; 35 if (userPattern.test(line) && line.includes('*') && i + 1 < lines.length) { 36 const user = line; 37 const content = lines[i + 1]; 38 if (content.length < 5 || content.match(/^(全部评价|问大家|查看更多)/)) continue; 39 results.push({ 40 rank: results.length + 1, 41 user, 42 content: content.slice(0, 150), 43 date: '', 44 }); 45 i++; 46 if (results.length >= ${limit}) break; 47 } 48 } 49 return results; 50 })() 51 `); 52 return Array.isArray(data) ? data : []; 53 }, 54 });