page-analyzer 1.0.0 → 1.0.1
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.
- package/README.md +400 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# page-analyzer
|
|
2
|
+
|
|
3
|
+
`page-analyzer` 是一个独立的网页分析模块,用于抓取网页、解析 DOM、提取交互元素,并结合 OpenAI 兼容的 LLM 接口生成页面区块与事件分析结果。
|
|
4
|
+
|
|
5
|
+
它适合用于:
|
|
6
|
+
|
|
7
|
+
- 分析页面上的链接、按钮、表单、输入框等可交互节点
|
|
8
|
+
- 将页面 DOM 元素转换为结构化 CSV,便于后续分析
|
|
9
|
+
- 基于视觉区块和上下文识别页面主要功能区
|
|
10
|
+
- 通过 LLM 判断节点可能触发的事件类型
|
|
11
|
+
- 为埋点设计、页面巡检、自动化测试或站点地图分析提供基础数据
|
|
12
|
+
|
|
13
|
+
## 功能概览
|
|
14
|
+
|
|
15
|
+
- 使用 Playwright 启动 headless Chromium 抓取真实页面内容
|
|
16
|
+
- 自动滚动页面并等待高度稳定,尽量覆盖懒加载内容
|
|
17
|
+
- 解析 HTML 中的重要元素,包括 `a`、`button`、`form`、`input`、`select`、`textarea`、标题、图片和导航区
|
|
18
|
+
- 收集元素文本、链接、表单动作、ARIA 信息、上下文文本、CSS 选择器和视觉区块位置
|
|
19
|
+
- 将交互元素导出为 CSV:`idx,blockIdx,tag,imageAlt,text,context,href`
|
|
20
|
+
- 使用 OpenAI 兼容接口进行页面区块分析和事件类型识别
|
|
21
|
+
- 支持紧凑输出或完整输出,便于在不同场景控制结果体积
|
|
22
|
+
- 支持高级用法:直接传入已抓取的 HTML、blocks、elementGeometries 进行分析
|
|
23
|
+
|
|
24
|
+
## 安装
|
|
25
|
+
|
|
26
|
+
从 npm 安装:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install page-analyzer
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
如果在本仓库内开发:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Playwright 需要可用的 Chromium。如果运行时提示浏览器缺失,可以执行:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx playwright install chromium
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 环境要求
|
|
45
|
+
|
|
46
|
+
- Node.js 20.18.1 或更高版本
|
|
47
|
+
- 可访问目标网页的网络环境
|
|
48
|
+
- 一个兼容 OpenAI Chat Completions 格式的 LLM 接口
|
|
49
|
+
|
|
50
|
+
## 快速开始
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
import { analyzeUrl } from 'page-analyzer';
|
|
54
|
+
|
|
55
|
+
const result = await analyzeUrl('https://example.com', {
|
|
56
|
+
llm: {
|
|
57
|
+
apiKey: process.env.LLM_API_KEY,
|
|
58
|
+
apiEndpoint: process.env.LLM_API_ENDPOINT,
|
|
59
|
+
model: process.env.LLM_MODEL
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
console.log(JSON.stringify(result, null, 2));
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
示例 `.env`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
LLM_API_KEY=your_api_key
|
|
70
|
+
LLM_API_ENDPOINT=https://api.openai.com/v1/chat/completions
|
|
71
|
+
LLM_MODEL=gpt-4o-mini
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 运行示例脚本
|
|
75
|
+
|
|
76
|
+
仓库内提供了 `test.js` 作为本地调试入口。它会读取项目根目录下的 `.env`,分析指定 URL,并把结果写入 `result.json`。
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm test
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
也可以指定 URL:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node test.js https://example.com
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
注意:`test.js` 依赖以下环境变量:
|
|
89
|
+
|
|
90
|
+
- `LLM_API_KEY`
|
|
91
|
+
- `LLM_API_ENDPOINT`
|
|
92
|
+
- `LLM_MODEL`
|
|
93
|
+
|
|
94
|
+
缺少这些变量时会抛出配置错误。
|
|
95
|
+
|
|
96
|
+
## API
|
|
97
|
+
|
|
98
|
+
### analyzeUrl(url, options)
|
|
99
|
+
|
|
100
|
+
一站式分析入口。传入 URL 后,模块会自动完成页面抓取、HTML 解析、视觉区块提取、CSV 生成和 LLM 分析。
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
import { analyzeUrl } from 'page-analyzer';
|
|
104
|
+
|
|
105
|
+
const result = await analyzeUrl('https://example.com', {
|
|
106
|
+
llm: {
|
|
107
|
+
apiKey: 'sk-...',
|
|
108
|
+
apiEndpoint: 'https://api.openai.com/v1/chat/completions',
|
|
109
|
+
model: 'gpt-4o-mini',
|
|
110
|
+
temperature: 0,
|
|
111
|
+
maxTokens: 8000,
|
|
112
|
+
timeout: 600000,
|
|
113
|
+
maxRetries: 3
|
|
114
|
+
},
|
|
115
|
+
showEvents: true,
|
|
116
|
+
showBlockIdx: true,
|
|
117
|
+
knownEventTypes: ['click_link', 'submit_form'],
|
|
118
|
+
extractorConfig: {
|
|
119
|
+
viewportWidth: 1440,
|
|
120
|
+
viewportHeight: 900,
|
|
121
|
+
timeoutMs: 30000
|
|
122
|
+
},
|
|
123
|
+
parserConfig: {
|
|
124
|
+
contextLevel: 'full',
|
|
125
|
+
maxTextLength: 200
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
参数说明:
|
|
131
|
+
|
|
132
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| `url` | `string` | 是 | 要分析的页面 URL |
|
|
135
|
+
| `options.llm.apiKey` | `string` | 是 | LLM API key |
|
|
136
|
+
| `options.llm.apiEndpoint` | `string` | 是 | OpenAI 兼容的 Chat Completions endpoint |
|
|
137
|
+
| `options.llm.model` | `string` | 是 | 模型名称 |
|
|
138
|
+
| `options.llm.temperature` | `number` | 否 | LLM 温度,默认 `0` |
|
|
139
|
+
| `options.llm.maxTokens` | `number` | 否 | 最大输出 token 数 |
|
|
140
|
+
| `options.llm.timeout` | `number` | 否 | 单次请求超时时间,默认 `600000` ms |
|
|
141
|
+
| `options.llm.maxRetries` | `number` | 否 | LLM 请求重试次数,默认 `3` |
|
|
142
|
+
| `options.llm.interactionLogger` | `Function` | 否 | LLM 交互日志回调 |
|
|
143
|
+
| `options.knownEventTypes` | `string[]` | 否 | 已知事件类型,用于判断新增事件类型 |
|
|
144
|
+
| `options.parserConfig` | `object` | 否 | HTML 解析配置 |
|
|
145
|
+
| `options.extractorConfig` | `object` | 否 | Playwright 页面抓取配置 |
|
|
146
|
+
| `options.showEvents` | `boolean` | 否 | 是否返回完整事件数组和元素明细 |
|
|
147
|
+
| `options.showBlockIdx` | `boolean` | 否 | 是否返回 CSV 与区块索引相关字段 |
|
|
148
|
+
|
|
149
|
+
### analyzePageEvents(input)
|
|
150
|
+
|
|
151
|
+
高级入口。适合你已经有 HTML、视觉区块或元素几何信息,不希望 `page-analyzer` 自己打开浏览器抓取页面的场景。
|
|
152
|
+
|
|
153
|
+
```js
|
|
154
|
+
import { analyzePageEvents } from 'page-analyzer';
|
|
155
|
+
|
|
156
|
+
const result = await analyzePageEvents({
|
|
157
|
+
html: '<html><head><title>Demo</title></head><body><a href="/pricing">Pricing</a></body></html>',
|
|
158
|
+
url: 'https://example.com',
|
|
159
|
+
blocks: [],
|
|
160
|
+
elementGeometries: [],
|
|
161
|
+
llm: {
|
|
162
|
+
apiKey: process.env.LLM_API_KEY,
|
|
163
|
+
apiEndpoint: process.env.LLM_API_ENDPOINT,
|
|
164
|
+
model: process.env.LLM_MODEL
|
|
165
|
+
},
|
|
166
|
+
knownEventTypes: [],
|
|
167
|
+
parserConfig: {
|
|
168
|
+
contextLevel: 'lean'
|
|
169
|
+
},
|
|
170
|
+
showEvents: true,
|
|
171
|
+
showBlockIdx: true,
|
|
172
|
+
domain: 'example.com',
|
|
173
|
+
nodeId: 'example-root'
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
主要输入:
|
|
178
|
+
|
|
179
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
180
|
+
| --- | --- | --- | --- |
|
|
181
|
+
| `html` | `string` | 是 | 原始 HTML |
|
|
182
|
+
| `url` | `string` | 是 | 页面 URL,用于解析相对链接 |
|
|
183
|
+
| `blocks` | `Array` | 否 | 视觉区块快照 |
|
|
184
|
+
| `elementGeometries` | `Array` | 否 | 页面中交互元素的几何信息 |
|
|
185
|
+
| `llm` | `object` | 是 | LLM 配置 |
|
|
186
|
+
| `knownEventTypes` | `string[]` | 否 | 已知事件类型 |
|
|
187
|
+
| `parserConfig` | `object` | 否 | HTML 解析配置 |
|
|
188
|
+
| `showEvents` | `boolean` | 否 | 是否返回完整事件数组和元素明细 |
|
|
189
|
+
| `showBlockIdx` | `boolean` | 否 | 是否返回 CSV 与区块索引相关字段 |
|
|
190
|
+
| `domain` | `string` | 否 | 分析上下文中的站点域名 |
|
|
191
|
+
| `nodeId` | `string` | 否 | 当前分析节点 ID |
|
|
192
|
+
|
|
193
|
+
如果没有传入 `blocks`,模块会根据 CSV 行构造 fallback block,保证 LLM 分析流程仍然可以运行。
|
|
194
|
+
|
|
195
|
+
## 输出结构
|
|
196
|
+
|
|
197
|
+
默认输出会保留页面标题、解析指标和紧凑后的区块分析结果:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
{
|
|
201
|
+
title: 'Example Domain',
|
|
202
|
+
parseMetrics: {
|
|
203
|
+
parseMs: 12,
|
|
204
|
+
contextBuildMs: 5,
|
|
205
|
+
elementsCount: 10,
|
|
206
|
+
linksCount: 3,
|
|
207
|
+
heapUsedMB: 28.4,
|
|
208
|
+
contextLevel: 'full'
|
|
209
|
+
},
|
|
210
|
+
analysis: {
|
|
211
|
+
block_analysis: {
|
|
212
|
+
site_summary: '...',
|
|
213
|
+
blocks: [
|
|
214
|
+
{
|
|
215
|
+
blockName: '...',
|
|
216
|
+
blockDescription: '...',
|
|
217
|
+
blockSemantics: [],
|
|
218
|
+
blockCssPath: '...',
|
|
219
|
+
blockPosition: {}
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
stats: {
|
|
223
|
+
total_blocks: 4
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
启用 `showEvents: true` 后,输出会包含更多用于调试和下游处理的字段,例如:
|
|
231
|
+
|
|
232
|
+
- `elements`:解析出的页面元素明细
|
|
233
|
+
- `csvContent`:交互元素 CSV
|
|
234
|
+
- `links`:页面链接列表
|
|
235
|
+
- `analysis.events_by_node`:按节点输出的事件识别结果
|
|
236
|
+
- `analysis.event_types_summary`:事件类型汇总
|
|
237
|
+
- `analysis.new_event_types`:不在已知类型列表中的新增事件类型
|
|
238
|
+
- `analysis.block_analysis.possible_event_types`:页面可能存在的事件类型
|
|
239
|
+
|
|
240
|
+
启用 `showBlockIdx: true` 后,区块结果中会额外包含 `blockIdxs`、`blockSemanticGroups`、`rowCount` 等字段,并返回 `csvContent`。
|
|
241
|
+
|
|
242
|
+
## 配置项
|
|
243
|
+
|
|
244
|
+
### extractorConfig
|
|
245
|
+
|
|
246
|
+
`extractorConfig` 控制 Playwright 抓取和视觉区块提取行为。
|
|
247
|
+
|
|
248
|
+
| 字段 | 默认值 | 说明 |
|
|
249
|
+
| --- | --- | --- |
|
|
250
|
+
| `timeoutMs` | `30000` | 页面导航、滚动和稳定等待超时时间 |
|
|
251
|
+
| `viewportWidth` | `1440` | 浏览器视口宽度 |
|
|
252
|
+
| `viewportHeight` | `900` | 浏览器视口高度 |
|
|
253
|
+
| `minBlockHeight` | `40` | 最小区块高度 |
|
|
254
|
+
| `minBlockWidthRatio` | `0.25` | 最小区块宽度占视口宽度比例 |
|
|
255
|
+
| `blockMaxHeightRatio` | `1.5` | 最大区块高度占视口高度比例 |
|
|
256
|
+
| `blockMaxDepth` | `15` | 区块提取最大 DOM 深度 |
|
|
257
|
+
| `textPreviewMaxChars` | `1200` | 区块文本预览最大长度 |
|
|
258
|
+
|
|
259
|
+
### parserConfig
|
|
260
|
+
|
|
261
|
+
`parserConfig` 控制 HTML 元素解析和上下文提取行为。
|
|
262
|
+
|
|
263
|
+
| 字段 | 默认值 | 说明 |
|
|
264
|
+
| --- | --- | --- |
|
|
265
|
+
| `importantTags` | 内置标签列表 | 要提取的重要 DOM 标签 |
|
|
266
|
+
| `maxTextLength` | `200` | 元素自身文本最大长度 |
|
|
267
|
+
| `maxParentTextLength` | `100` | 父级文本最大长度 |
|
|
268
|
+
| `maxAncestorDepth` | `5` | 祖先上下文最大深度 |
|
|
269
|
+
| `maxNearbyTexts` | `5` | 附近文本最大数量 |
|
|
270
|
+
| `contextLevel` | `full` | 上下文详细程度,可选 `full` 或 `lean` |
|
|
271
|
+
|
|
272
|
+
## 高级组件
|
|
273
|
+
|
|
274
|
+
除主 API 外,模块还导出了一些内部组件,方便按需组合:
|
|
275
|
+
|
|
276
|
+
```js
|
|
277
|
+
import {
|
|
278
|
+
HtmlParser,
|
|
279
|
+
PageExtractor,
|
|
280
|
+
CsvExporter,
|
|
281
|
+
OpenAiProvider,
|
|
282
|
+
BaseLlmProvider,
|
|
283
|
+
EventAnalyzer,
|
|
284
|
+
assignBlocksToElements
|
|
285
|
+
} from 'page-analyzer';
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
这些组件可以用于自定义分析流水线,例如只抓页面、只解析 HTML、只生成 CSV,或接入自定义 LLM provider。
|
|
289
|
+
|
|
290
|
+
## LLM 接口格式
|
|
291
|
+
|
|
292
|
+
当前内置的 `OpenAiProvider` 会向 `apiEndpoint` 发送 OpenAI Chat Completions 风格请求:
|
|
293
|
+
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"model": "gpt-4o-mini",
|
|
297
|
+
"messages": [
|
|
298
|
+
{
|
|
299
|
+
"role": "user",
|
|
300
|
+
"content": "..."
|
|
301
|
+
}
|
|
302
|
+
],
|
|
303
|
+
"temperature": 0
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
响应中会读取:
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
data.choices[0].message.content
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
因此,只要服务兼容这一请求和响应结构,就可以作为后端 LLM 使用。
|
|
314
|
+
|
|
315
|
+
## 开发
|
|
316
|
+
|
|
317
|
+
安装依赖:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
npm install
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
运行测试脚本:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
npm test
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
生成 npm 发布包预览:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
npm pack --dry-run
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
发布前检查:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
npm publish --dry-run
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## 项目结构
|
|
342
|
+
|
|
343
|
+
```text
|
|
344
|
+
page-analyzer/
|
|
345
|
+
index.js # 模块入口,导出主 API 和高级组件
|
|
346
|
+
page-extractor.js # Playwright 页面抓取与视觉区块提取
|
|
347
|
+
html-parser.js # HTML 解析与元素上下文提取
|
|
348
|
+
csv-exporter.js # 交互元素 CSV 生成
|
|
349
|
+
extractors/ # 区块映射、上下文提取、选择器构建
|
|
350
|
+
llm/
|
|
351
|
+
providers/ # LLM provider
|
|
352
|
+
analyzers/event-analyzer/ # 页面区块和事件分析逻辑
|
|
353
|
+
analyzers/prompts/ # LLM prompt 模板
|
|
354
|
+
utils/ # 事件 CSV 解析和元数据工具
|
|
355
|
+
models/ # 上下文数据模型
|
|
356
|
+
utils/ # 文本、URL、选择器工具
|
|
357
|
+
vendor/ # 浏览器内区块提取脚本
|
|
358
|
+
test.js # 本地调试脚本
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## 常见问题
|
|
362
|
+
|
|
363
|
+
### npm test 报 LLM 配置缺失
|
|
364
|
+
|
|
365
|
+
确认项目根目录存在 `.env`,并且包含:
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
LLM_API_KEY=your_api_key
|
|
369
|
+
LLM_API_ENDPOINT=https://api.openai.com/v1/chat/completions
|
|
370
|
+
LLM_MODEL=gpt-4o-mini
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Playwright 报浏览器不存在
|
|
374
|
+
|
|
375
|
+
执行:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
npx playwright install chromium
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### 目标页面内容不完整
|
|
382
|
+
|
|
383
|
+
可以适当调大:
|
|
384
|
+
|
|
385
|
+
```js
|
|
386
|
+
extractorConfig: {
|
|
387
|
+
timeoutMs: 60000,
|
|
388
|
+
viewportHeight: 1200
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
模块会自动滚动到底部并等待页面高度稳定,但如果目标站点需要登录、验证码、复杂交互或服务端限流,仍然可能拿不到完整内容。
|
|
393
|
+
|
|
394
|
+
### LLM 返回格式解析失败
|
|
395
|
+
|
|
396
|
+
事件分析器期望 LLM 返回 CSV section。内部会尝试自动修复格式,但如果模型输出长期不稳定,可以换用更强的模型,或降低温度。
|
|
397
|
+
|
|
398
|
+
## License
|
|
399
|
+
|
|
400
|
+
当前仓库没有声明 License。发布到 npm 前建议补充明确的开源许可证,例如 MIT、Apache-2.0 或私有许可证说明。
|