@xbrowser/cmf-seats 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.ts +319 -0
  2. package/package.json +14 -0
package/index.ts ADDED
@@ -0,0 +1,319 @@
1
+ import { ok, fail } from '@dyyz1993/xcli-core';
2
+
3
+ /**
4
+ * xbrowser 插件:座椅CMF评论查询
5
+ * 查询指定车型的座椅颜色/材质/触感相关评论
6
+ */
7
+
8
+ import type { XCLIAPI } from '@dyyz1993/xcli-core';
9
+ import { z } from 'zod';
10
+
11
+ // CMF相关关键词
12
+ const CMF_KEYWORDS = [
13
+ '座椅', '真皮', 'Nappa', 'Alcantara', '材质', '面料',
14
+ '颜色', '配色', '棕色', '黑色', '白色', '米色', '红色',
15
+ '触感', '手感', '柔软', '硬', '舒适', '包裹性',
16
+ '缝线', '皮', '布', '织物', '透气', '质感',
17
+ '头枕', '腰托', '腿托', '加热', '通风', '按摩',
18
+ '座椅材质', '座椅颜色', '座椅触感', '座椅舒适性',
19
+ '巴赫座椅', '云毯座椅', 'Sofaro座椅', '航空座椅',
20
+ '零压座椅', '美姿座椅', '按摩', '通风', '加热',
21
+ '腰靠', '头等舱', '包裹', '支撑', '柔软', '硬', '弹',
22
+ '坐感', '靠背', '坐垫', '承托', '贴合', '悬浮',
23
+ '大沙发', '零压云毯', 'AI零压', '气囊', '支撑感',
24
+ '腰部支撑', '腿部支撑', '背部支撑', '坐姿', '乘坐',
25
+ '乘坐体验', '乘坐感受', '乘坐舒适', '座椅配置',
26
+ '座椅功能', '座椅调节', '座椅材质', '座椅皮质',
27
+ '座椅面料', '座椅设计', '座椅包裹', '座椅支撑',
28
+ '皮革', '合成皮', '仿皮', '布艺', '织物座椅',
29
+ '真皮座椅', 'Nappa真皮', '半苯胺', 'Dinamica',
30
+ 'Microsuede', 'Ultrasuede', '麂皮', '翻毛皮',
31
+ '内饰', '内饰材质', '内饰配色', '内饰颜色',
32
+ '内饰质感', '内饰用料', '豪华感', '高级感',
33
+ '触手可及', '摸起来', '手感好', '触感细腻',
34
+ '手感细腻', '触感冰凉', '触感温润', '质感很好',
35
+ '做工精细', '做工精致', '缝线整齐', '缝线工整',
36
+ '工艺精湛', '做工考究', '用料扎实', '用料厚道'
37
+ ];
38
+
39
+ // 车型配置
40
+ const CARS = [
41
+ { id: '7935', name: '日产N7' },
42
+ { id: '8291', name: '岚图泰山' },
43
+ { id: '6925', name: '飞凡F7' },
44
+ { id: '4176', name: '捷尼赛思G80' },
45
+ { id: '59', name: '奔驰S级' },
46
+ { id: '146', name: '奥迪A8L' },
47
+ { id: '6846', name: '极氪009' },
48
+ { id: '75', name: '宝马5系' },
49
+ { id: '86', name: '奥迪A6L' },
50
+ { id: '3619', name: '雷克萨斯ES' },
51
+ { id: '1669', name: '凯迪拉克CT6' },
52
+ { id: '403', name: '雷克萨斯LS' },
53
+ { id: '2205', name: '沃尔沃S90' },
54
+ { id: '3281', name: '保时捷Panamera' },
55
+ { id: '623', name: '理想L7' },
56
+ { id: '624', name: '理想L9' },
57
+ { id: '4088', name: '蔚来ES6' },
58
+ { id: '4087', name: '蔚来ET7' },
59
+ { id: '4255', name: '小鹏P7' },
60
+ { id: '6282', name: '问界M9' },
61
+ { id: '6301', name: '问界S9' },
62
+ { id: '174', name: '宝马X3' },
63
+ { id: '1815', name: '宝马X5' },
64
+ { id: '1670', name: '奔驰GLC' },
65
+ { id: '1723', name: '奔驰GLE' },
66
+ { id: '3274', name: '保时捷Cayenne' },
67
+ { id: '383', name: '沃尔沃XC90' },
68
+ { id: '77', name: '宝马3系' },
69
+ { id: '88', name: '奥迪A4L' },
70
+ { id: '164', name: '奔驰C级' },
71
+ { id: '3957', name: '奔驰GLB' },
72
+ { id: '4217', name: '凯迪拉克CT5' },
73
+ { id: '61', name: '大众迈腾' },
74
+ { id: '615', name: '比亚迪汉' },
75
+ { id: '5185', name: '比亚迪海豹' },
76
+ { id: '6642', name: '小米SU7' },
77
+ { id: '6772', name: '极氪001' },
78
+ { id: '4198', name: '特斯拉Model 3' },
79
+ { id: '395', name: '别克GL8' },
80
+ { id: '4290', name: '腾势D9' },
81
+ { id: '4253', name: '岚图梦想家' },
82
+ { id: '4219', name: '魏牌高山' }
83
+ ];
84
+
85
+ // 检查是否包含CMF关键词
86
+ function hasCMFKeyword(text: string): boolean {
87
+ const lowerText = text.toLowerCase();
88
+ return CMF_KEYWORDS.some(kw => lowerText.includes(kw.toLowerCase()));
89
+ }
90
+
91
+ // 提取CMF关键词
92
+ function extractKeywords(text: string): string[] {
93
+ const keywords: string[] = [];
94
+ const lowerText = text.toLowerCase();
95
+ for (const kw of CMF_KEYWORDS) {
96
+ if (lowerText.includes(kw.toLowerCase()) && !keywords.includes(kw)) {
97
+ keywords.push(kw);
98
+ }
99
+ }
100
+ return keywords;
101
+ }
102
+
103
+ export default function(api: XCLIAPI): void {
104
+ const site = api.createSite({
105
+ name: 'cmf-seats',
106
+ url: 'https://k.m.autohome.com.cn',
107
+ description: '座椅CMF评论查询(颜色/材质/触感)'
108
+ });
109
+
110
+ // 查询车型的CMF评论
111
+ site.command('query', {
112
+ description: '查询指定车型的座椅CMF评论',
113
+ scope: 'page',
114
+ parameters: z.object({
115
+ car: z.string().describe('车型名称(如:日产N7、奔驰S级、奥迪A8L)'),
116
+ keyword: z.string().optional().describe('CMF关键词(如:座椅、真皮、舒适)'),
117
+ limit: z.number().min(1).max(100).default(10).describe('返回结果数量限制')
118
+ }),
119
+ result: z.object({
120
+ success: z.boolean(),
121
+ data: z.object({
122
+ car: z.string(),
123
+ car_id: z.string(),
124
+ total: z.number(),
125
+ returned: z.number(),
126
+ keyword: z.string().nullable(),
127
+ reviews: z.array(z.object({
128
+ car: z.string(),
129
+ content: z.string(),
130
+ keywords: z.array(z.string()),
131
+ timestamp: z.string()
132
+ }))
133
+ }),
134
+ tips: z.array(z.string())
135
+ }),
136
+ handler: async (params, ctx) => {
137
+ const page = (ctx as any).page as any;
138
+
139
+ // 查找车型ID
140
+ const car = CARS.find(c => c.name === params.car);
141
+ if (!car) {
142
+ return fail({ data: null as any,
143
+ tips: [`未找到车型 "${params.car}",支持的车型:${CARS.slice(0, 10).map(c => c.name).join(', ')}等${CARS.length}个车型`]
144
+ });
145
+ }
146
+
147
+ // 从已爬取的数据中查询
148
+ try {
149
+ const fs = await import('fs');
150
+ const path = await import('path');
151
+ const { fileURLToPath } = await import('url');
152
+
153
+ const __filename = fileURLToPath(import.meta.url);
154
+ const __dirname = path.dirname(__filename);
155
+ const dataFile = path.join(__dirname, '..', '..', 'output', 'cmf_seat_reviews_batch.json');
156
+
157
+ if (!fs.existsSync(dataFile)) {
158
+ return fail({ data: null as any,
159
+ tips: [`CMF评论数据文件不存在,请先运行批量爬取脚本`]
160
+ });
161
+ }
162
+
163
+ const rawData = fs.readFileSync(dataFile, 'utf-8');
164
+ const data = JSON.parse(rawData);
165
+
166
+ // 过滤指定车型的评论
167
+ let reviews = data.reviews.filter((r: any) => r.car === params.car);
168
+
169
+ // 按关键词过滤
170
+ if (params.keyword) {
171
+ reviews = reviews.filter((r: any) =>
172
+ r.content.toLowerCase().includes(params.keyword.toLowerCase()) ||
173
+ r.keywords?.some((k: string) => k.toLowerCase().includes(params.keyword.toLowerCase()))
174
+ );
175
+ }
176
+
177
+ // 限制返回数量
178
+ const limitedReviews = reviews.slice(0, params.limit);
179
+
180
+ return ok({ data: {
181
+ car: params.car,
182
+ car_id: car.id,
183
+ total: reviews.length,
184
+ returned: limitedReviews.length,
185
+ keyword: params.keyword || null,
186
+ reviews: limitedReviews.map((r: any) => ({
187
+ car: r.car,
188
+ content: r.content,
189
+ keywords: r.keywords || [],
190
+ timestamp: r.timestamp
191
+ }))
192
+ } as any,
193
+ tips: [
194
+ `找到 ${reviews.length} 条${params.car}的CMF评论`,
195
+ limitedReviews.length < reviews.length ? `(返回前${limitedReviews.length}条)` : ''
196
+ ]
197
+ });
198
+ } catch (error) {
199
+ return fail({ data: null as any,
200
+ tips: [`查询失败: ${(error as Error).message}`]
201
+ });
202
+ }
203
+ }
204
+ });
205
+
206
+ // 列出所有支持的车型
207
+ site.command('list', {
208
+ description: '列出所有支持的车型',
209
+ scope: 'any',
210
+ parameters: z.object({}),
211
+ result: z.object({
212
+ success: z.boolean(),
213
+ data: z.object({
214
+ total: z.number(),
215
+ cars: z.array(z.object({
216
+ id: z.string(),
217
+ name: z.string(),
218
+ autohome_url: z.string()
219
+ }))
220
+ }),
221
+ tips: z.array(z.string())
222
+ }),
223
+ handler: async (params, ctx) => {
224
+ return ok({ data: {
225
+ total: CARS.length,
226
+ cars: CARS.map(c => ({
227
+ id: c.id,
228
+ name: c.name,
229
+ autohome_url: `https://k.m.autohome.com.cn/${c.id}/`
230
+ }))
231
+ } as any,
232
+ tips: [
233
+ `共支持 ${CARS.length} 个车型`,
234
+ `使用 "cmf-seats query --car <车型名称>" 查询CMF评论`
235
+ ]
236
+ });
237
+ }
238
+ });
239
+
240
+ // 统计CMF关键词频率
241
+ site.command('stats', {
242
+ description: '统计CMF关键词频率',
243
+ scope: 'page',
244
+ parameters: z.object({
245
+ car: z.string().optional().describe('车型名称,不指定则统计所有车型'),
246
+ top: z.number().min(5).max(50).default(20).describe('返回Top N关键词')
247
+ }),
248
+ result: z.object({
249
+ success: z.boolean(),
250
+ data: z.object({
251
+ car: z.string(),
252
+ total_reviews: z.number(),
253
+ total_keywords: z.number(),
254
+ top_keywords: z.array(z.object({
255
+ keyword: z.string(),
256
+ count: z.number()
257
+ }))
258
+ }),
259
+ tips: z.array(z.string())
260
+ }),
261
+ handler: async (params, ctx) => {
262
+ try {
263
+ const fs = await import('fs');
264
+ const path = await import('path');
265
+ const { fileURLToPath } = await import('url');
266
+
267
+ const __filename = fileURLToPath(import.meta.url);
268
+ const __dirname = path.dirname(__filename);
269
+ const dataFile = path.join(__dirname, '..', '..', 'output', 'cmf_seat_reviews_batch.json');
270
+
271
+ if (!fs.existsSync(dataFile)) {
272
+ return fail({ data: null as any,
273
+ tips: [`CMF评论数据文件不存在,请先运行批量爬取脚本`]
274
+ });
275
+ }
276
+
277
+ const rawData = fs.readFileSync(dataFile, 'utf-8');
278
+ const data = JSON.parse(rawData);
279
+
280
+ // 过滤指定车型
281
+ let reviews = data.reviews;
282
+ if (params.car) {
283
+ reviews = reviews.filter((r: any) => r.car === params.car);
284
+ }
285
+
286
+ // 统计关键词频率
287
+ const keywordCounts = new Map<string, number>();
288
+ reviews.forEach((r: any) => {
289
+ (r.keywords || []).forEach((kw: string) => {
290
+ const count = keywordCounts.get(kw) || 0;
291
+ keywordCounts.set(kw, count + 1);
292
+ });
293
+ });
294
+
295
+ // 排序
296
+ const sortedKeywords = Array.from(keywordCounts.entries())
297
+ .sort((a, b) => b[1] - a[1])
298
+ .slice(0, params.top);
299
+
300
+ return ok({ data: {
301
+ car: params.car || 'all',
302
+ total_reviews: reviews.length,
303
+ total_keywords: keywordCounts.size,
304
+ top_keywords: sortedKeywords.map(([kw, count]) => ({ keyword: kw, count }))
305
+ } as any,
306
+ tips: [
307
+ `${params.car ? params.car : '所有车型'} 共 ${reviews.length} 条评论`,
308
+ `包含 ${keywordCounts.size} 个不同的CMF关键词`,
309
+ `显示前 ${sortedKeywords.length} 个关键词`
310
+ ]
311
+ });
312
+ } catch (error) {
313
+ return fail({ data: null as any,
314
+ tips: [`统计失败: ${(error as Error).message}`]
315
+ });
316
+ }
317
+ }
318
+ });
319
+ }
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@xbrowser/cmf-seats",
3
+ "version": "1.0.0",
4
+ "description": "座椅CMF评论查询(颜色/材质/触感)- 从821条真实车主评论中提取",
5
+ "main": "index.ts",
6
+ "type": "module",
7
+ "xbrowser": {
8
+ "name": "cmf-seats",
9
+ "description": "座椅CMF评论查询(颜色/材质/触感)- 从821条真实车主评论中提取"
10
+ },
11
+ "dependencies": {
12
+ "zod": "^3.22.4"
13
+ }
14
+ }