detail.js
1 import { cli, Strategy } from '@jackwener/opencli/registry'; 2 import { normalizeNumericId } from '../_shared/common.js'; 3 cli({ 4 site: 'jd', 5 name: 'detail', 6 description: '京东商品详情', 7 domain: 'item.jd.com', 8 strategy: Strategy.COOKIE, 9 args: [ 10 { name: 'sku', positional: true, required: true, help: '商品 SKU ID' }, 11 ], 12 columns: ['field', 'value'], 13 navigateBefore: false, 14 func: async (page, kwargs) => { 15 const sku = normalizeNumericId(kwargs.sku, 'sku', '100291143898'); 16 await page.goto(`https://item.jd.com/${sku}.html`); 17 await page.wait(5); 18 const data = await page.evaluate(` 19 (() => { 20 const normalize = v => (v || '').replace(/\\s+/g, ' ').trim(); 21 const text = document.body?.innerText || ''; 22 23 const titleMatch = document.title.match(/^【[^】]*】(.+?)【/); 24 const title = titleMatch ? titleMatch[1].trim() : normalize(document.title.split('【')[0]); 25 26 const priceMatch = text.match(/¥([\\d,.]+)/); 27 const price = priceMatch ? '¥' + priceMatch[1] : ''; 28 29 const ratingMatch = text.match(/(超\\d+%[^\\n]{2,20})/); 30 const rating = ratingMatch ? ratingMatch[1] : ''; 31 32 const reviewMatch = text.match(/买家评价\\(([\\d万+]+)\\)/); 33 const reviews = reviewMatch ? reviewMatch[1] : ''; 34 35 const shopMatch = text.match(/(\\S{2,15}(?:京东自营旗舰店|旗舰店|专卖店|自营店))/); 36 const shop = shopMatch ? shopMatch[1] : ''; 37 38 const tagPattern = /([\u4e00-\u9fa5]{2,8})\\s+(\\d+)/g; 39 const tags = []; 40 let m; 41 const tagStart = text.indexOf('买家评价'); 42 const tagSection = tagStart >= 0 ? text.substring(tagStart, tagStart + 500) : ''; 43 while ((m = tagPattern.exec(tagSection)) && tags.length < 6) { 44 if (parseInt(m[2], 10) > 1) tags.push(m[1] + '(' + m[2] + ')'); 45 } 46 47 const results = [ 48 { field: '商品名称', value: title }, 49 { field: '价格', value: price }, 50 { field: 'SKU', value: ${JSON.stringify(sku)} }, 51 { field: '店铺', value: shop }, 52 { field: '评价数量', value: reviews }, 53 { field: '好评率', value: rating }, 54 { field: '评价标签', value: tags.join(' | ') }, 55 { field: '链接', value: location.href }, 56 ]; 57 return results.filter(r => r.value); 58 })() 59 `); 60 return Array.isArray(data) ? data : []; 61 }, 62 });