page-analyzer 1.0.0 → 1.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 page-analyzer contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,430 @@
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
+ 本地测试不会调用真实网页或 LLM 接口:
77
+
78
+ ```bash
79
+ npm test
80
+ ```
81
+
82
+ 如需手动分析真实页面,可以运行示例脚本。它会读取项目根目录下的 `.env`,分析指定 URL,并把结果写入 `result.json`。
83
+
84
+ ```bash
85
+ npm run analyze -- https://example.com
86
+ ```
87
+
88
+ 注意:`npm run analyze` 依赖以下环境变量:
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
+ fullPageScreenshot: true,
118
+ blockScreenshots: true,
119
+ waitForImagesLoaded: true,
120
+ knownEventTypes: ['click_link', 'submit_form'],
121
+ extractorConfig: {
122
+ viewportWidth: 1440,
123
+ viewportHeight: 900,
124
+ timeoutMs: 30000
125
+ },
126
+ parserConfig: {
127
+ contextLevel: 'full',
128
+ maxTextLength: 200
129
+ }
130
+ });
131
+ ```
132
+
133
+ 参数说明:
134
+
135
+ | 参数 | 类型 | 必填 | 说明 |
136
+ | --- | --- | --- | --- |
137
+ | `url` | `string` | 是 | 要分析的页面 URL |
138
+ | `options.llm.apiKey` | `string` | 是 | LLM API key |
139
+ | `options.llm.apiEndpoint` | `string` | 是 | OpenAI 兼容的 Chat Completions endpoint |
140
+ | `options.llm.model` | `string` | 是 | 模型名称 |
141
+ | `options.llm.temperature` | `number` | 否 | LLM 温度,默认 `0` |
142
+ | `options.llm.maxTokens` | `number` | 否 | 最大输出 token 数 |
143
+ | `options.llm.timeout` | `number` | 否 | 单次请求超时时间,默认 `600000` ms |
144
+ | `options.llm.maxRetries` | `number` | 否 | LLM 请求重试次数,默认 `3` |
145
+ | `options.llm.interactionLogger` | `Function` | 否 | LLM 交互日志回调 |
146
+ | `options.knownEventTypes` | `string[]` | 否 | 已知事件类型,用于判断新增事件类型 |
147
+ | `options.parserConfig` | `object` | 否 | HTML 解析配置 |
148
+ | `options.extractorConfig` | `object` | 否 | Playwright 页面抓取配置 |
149
+ | `options.showEvents` | `boolean` | 否 | 是否返回完整事件数组和元素明细 |
150
+ | `options.showBlockIdx` | `boolean` | 否 | 是否返回 CSV 与区块索引相关字段 |
151
+ | `options.fullPageScreenshot` | `boolean` | 否 | 是否保存整页截图到当前运行目录的 `snapshots/` 并返回文件路径 |
152
+ | `options.blockScreenshots` | `boolean` | 否 | 是否在 LLM 合并区块后,保存每个逻辑区块截图到当前运行目录的 `snapshots/` 并返回文件路径 |
153
+ | `options.waitForImagesLoaded` | `boolean` | 否 | 是否在提取区块、分析和截图前等待页面图片加载完成,默认 `false` |
154
+
155
+ ### analyzePageEvents(input)
156
+
157
+ 高级入口。适合你已经有 HTML、视觉区块或元素几何信息,不希望 `page-analyzer` 自己打开浏览器抓取页面的场景。
158
+
159
+ ```js
160
+ import { analyzePageEvents } from 'page-analyzer';
161
+
162
+ const result = await analyzePageEvents({
163
+ html: '<html><head><title>Demo</title></head><body><a href="/pricing">Pricing</a></body></html>',
164
+ url: 'https://example.com',
165
+ blocks: [],
166
+ elementGeometries: [],
167
+ llm: {
168
+ apiKey: process.env.LLM_API_KEY,
169
+ apiEndpoint: process.env.LLM_API_ENDPOINT,
170
+ model: process.env.LLM_MODEL
171
+ },
172
+ knownEventTypes: [],
173
+ parserConfig: {
174
+ contextLevel: 'lean'
175
+ },
176
+ showEvents: true,
177
+ showBlockIdx: true,
178
+ domain: 'example.com',
179
+ nodeId: 'example-root'
180
+ });
181
+ ```
182
+
183
+ 主要输入:
184
+
185
+ | 字段 | 类型 | 必填 | 说明 |
186
+ | --- | --- | --- | --- |
187
+ | `html` | `string` | 是 | 原始 HTML |
188
+ | `url` | `string` | 是 | 页面 URL,用于解析相对链接 |
189
+ | `blocks` | `Array` | 否 | 视觉区块快照 |
190
+ | `elementGeometries` | `Array` | 否 | 页面中交互元素的几何信息 |
191
+ | `llm` | `object` | 是 | LLM 配置 |
192
+ | `knownEventTypes` | `string[]` | 否 | 已知事件类型 |
193
+ | `parserConfig` | `object` | 否 | HTML 解析配置 |
194
+ | `showEvents` | `boolean` | 否 | 是否返回完整事件数组和元素明细 |
195
+ | `showBlockIdx` | `boolean` | 否 | 是否返回 CSV 与区块索引相关字段 |
196
+ | `domain` | `string` | 否 | 分析上下文中的站点域名 |
197
+ | `nodeId` | `string` | 否 | 当前分析节点 ID |
198
+
199
+ 如果没有传入 `blocks`,模块会根据 CSV 行构造 fallback block,保证 LLM 分析流程仍然可以运行。
200
+
201
+ ## 输出结构
202
+
203
+ 默认输出会保留页面标题、解析指标和紧凑后的区块分析结果:
204
+
205
+ ```js
206
+ {
207
+ title: 'Example Domain',
208
+ parseMetrics: {
209
+ parseMs: 12,
210
+ contextBuildMs: 5,
211
+ elementsCount: 10,
212
+ linksCount: 3,
213
+ heapUsedMB: 28.4,
214
+ contextLevel: 'full'
215
+ },
216
+ analysis: {
217
+ block_analysis: {
218
+ site_summary: '...',
219
+ blocks: [
220
+ {
221
+ blockName: '...',
222
+ blockDescription: '...',
223
+ blockSemantics: [],
224
+ blockCssPath: '...',
225
+ blockPosition: {}
226
+ }
227
+ ],
228
+ stats: {
229
+ total_blocks: 4
230
+ }
231
+ }
232
+ }
233
+ }
234
+ ```
235
+
236
+ 启用 `showEvents: true` 后,输出会包含更多用于调试和下游处理的字段,例如:
237
+
238
+ - `elements`:解析出的页面元素明细
239
+ - `csvContent`:交互元素 CSV
240
+ - `links`:页面链接列表
241
+ - `analysis.events_by_node`:按节点输出的事件识别结果
242
+ - `analysis.event_types_summary`:事件类型汇总
243
+ - `analysis.new_event_types`:不在已知类型列表中的新增事件类型
244
+ - `analysis.block_analysis.possible_event_types`:页面可能存在的事件类型
245
+
246
+ 启用 `showBlockIdx: true` 后,区块结果中会额外包含 `blockIdxs`、`blockSemanticGroups`、`rowCount` 等字段,并返回 `csvContent`。
247
+
248
+ 启用 `fullPageScreenshot: true` 后,返回结果会包含 `screenshots.fullPage`,值为整页截图文件路径。
249
+
250
+ 启用 `blockScreenshots: true` 后,模块会在 LLM 合并区块后再截图。返回结果会包含 `screenshots.blocks`,每项包含逻辑区块序号 `blockIdx` 和对应截图 `path`;区块分析结果中的每个 block 也会额外带上 `blockScreenshotPaths`,每个逻辑区块最多对应一张截图。无法通过 `blockCssPath` 截图的隐藏或空区块会被跳过。
251
+
252
+ 启用 `waitForImagesLoaded: true` 后,模块会先滚动页面触发懒加载,再等待当前 DOM 中的 `<img>` 完成加载或失败,之后再提取区块、分析和截图;等待时间受 `extractorConfig.timeoutMs` 控制。
253
+
254
+ 截图参数启用后的新增输出示例:
255
+
256
+ ```js
257
+ {
258
+ screenshots: {
259
+ fullPage: '/path/to/page-analyzer/snapshots/example-com-20260507-095500-full-page.png',
260
+ blocks: [
261
+ {
262
+ blockIdx: 0,
263
+ path: '/path/to/page-analyzer/snapshots/example-com-20260507-095500-block-000.png'
264
+ }
265
+ ]
266
+ }
267
+ }
268
+ ```
269
+
270
+ ## 配置项
271
+
272
+ ### extractorConfig
273
+
274
+ `extractorConfig` 控制 Playwright 抓取和视觉区块提取行为。
275
+
276
+ | 字段 | 默认值 | 说明 |
277
+ | --- | --- | --- |
278
+ | `timeoutMs` | `30000` | 页面导航、滚动和稳定等待超时时间 |
279
+ | `viewportWidth` | `1440` | 浏览器视口宽度 |
280
+ | `viewportHeight` | `900` | 浏览器视口高度 |
281
+ | `minBlockHeight` | `40` | 最小区块高度 |
282
+ | `minBlockWidthRatio` | `0.25` | 最小区块宽度占视口宽度比例 |
283
+ | `blockMaxHeightRatio` | `1.5` | 最大区块高度占视口高度比例 |
284
+ | `blockMaxDepth` | `15` | 区块提取最大 DOM 深度 |
285
+ | `textPreviewMaxChars` | `1200` | 区块文本预览最大长度 |
286
+ | `waitForImagesLoaded` | `false` | 是否在提取区块、分析和截图前等待页面图片加载完成 |
287
+
288
+ ### parserConfig
289
+
290
+ `parserConfig` 控制 HTML 元素解析和上下文提取行为。
291
+
292
+ | 字段 | 默认值 | 说明 |
293
+ | --- | --- | --- |
294
+ | `importantTags` | 内置标签列表 | 要提取的重要 DOM 标签 |
295
+ | `maxTextLength` | `200` | 元素自身文本最大长度 |
296
+ | `maxParentTextLength` | `100` | 父级文本最大长度 |
297
+ | `maxAncestorDepth` | `5` | 祖先上下文最大深度 |
298
+ | `maxNearbyTexts` | `5` | 附近文本最大数量 |
299
+ | `contextLevel` | `full` | 上下文详细程度,可选 `full` 或 `lean` |
300
+
301
+ ## 高级组件
302
+
303
+ 除主 API 外,模块还导出了一些内部组件,方便按需组合:
304
+
305
+ ```js
306
+ import {
307
+ HtmlParser,
308
+ PageExtractor,
309
+ CsvExporter,
310
+ OpenAiProvider,
311
+ BaseLlmProvider,
312
+ EventAnalyzer,
313
+ assignBlocksToElements
314
+ } from 'page-analyzer';
315
+ ```
316
+
317
+ 这些组件可以用于自定义分析流水线,例如只抓页面、只解析 HTML、只生成 CSV,或接入自定义 LLM provider。
318
+
319
+ ## LLM 接口格式
320
+
321
+ 当前内置的 `OpenAiProvider` 会向 `apiEndpoint` 发送 OpenAI Chat Completions 风格请求:
322
+
323
+ ```json
324
+ {
325
+ "model": "gpt-4o-mini",
326
+ "messages": [
327
+ {
328
+ "role": "user",
329
+ "content": "..."
330
+ }
331
+ ],
332
+ "temperature": 0
333
+ }
334
+ ```
335
+
336
+ 响应中会读取:
337
+
338
+ ```js
339
+ data.choices[0].message.content
340
+ ```
341
+
342
+ 因此,只要服务兼容这一请求和响应结构,就可以作为后端 LLM 使用。
343
+
344
+ ## 开发
345
+
346
+ 安装依赖:
347
+
348
+ ```bash
349
+ npm install
350
+ ```
351
+
352
+ 运行本地测试:
353
+
354
+ ```bash
355
+ npm test
356
+ ```
357
+
358
+ 生成 npm 发布包预览:
359
+
360
+ ```bash
361
+ npm pack --dry-run
362
+ ```
363
+
364
+ 发布前检查:
365
+
366
+ ```bash
367
+ npm publish --dry-run
368
+ ```
369
+
370
+ ## 项目结构
371
+
372
+ ```text
373
+ page-analyzer/
374
+ index.js # 模块入口,导出主 API 和高级组件
375
+ page-extractor.js # Playwright 页面抓取与视觉区块提取
376
+ html-parser.js # HTML 解析与元素上下文提取
377
+ csv-exporter.js # 交互元素 CSV 生成
378
+ extractors/ # 区块映射、上下文提取、选择器构建
379
+ llm/
380
+ providers/ # LLM provider
381
+ analyzers/event-analyzer/ # 页面区块和事件分析逻辑
382
+ analyzers/prompts/ # LLM prompt 模板
383
+ utils/ # 事件 CSV 解析和元数据工具
384
+ models/ # 上下文数据模型
385
+ utils/ # 文本、URL、选择器工具
386
+ vendor/ # 浏览器内区块提取脚本
387
+ scripts/analyze.js # 手动真实页面分析脚本
388
+ test/smoke.test.js # 本地 smoke test
389
+ ```
390
+
391
+ ## 常见问题
392
+
393
+ ### npm run analyze 报 LLM 配置缺失
394
+
395
+ 确认项目根目录存在 `.env`,并且包含:
396
+
397
+ ```bash
398
+ LLM_API_KEY=your_api_key
399
+ LLM_API_ENDPOINT=https://api.openai.com/v1/chat/completions
400
+ LLM_MODEL=gpt-4o-mini
401
+ ```
402
+
403
+ ### Playwright 报浏览器不存在
404
+
405
+ 执行:
406
+
407
+ ```bash
408
+ npx playwright install chromium
409
+ ```
410
+
411
+ ### 目标页面内容不完整
412
+
413
+ 可以适当调大:
414
+
415
+ ```js
416
+ extractorConfig: {
417
+ timeoutMs: 60000,
418
+ viewportHeight: 1200
419
+ }
420
+ ```
421
+
422
+ 模块会自动滚动到底部并等待页面高度稳定,但如果目标站点需要登录、验证码、复杂交互或服务端限流,仍然可能拿不到完整内容。
423
+
424
+ ### LLM 返回格式解析失败
425
+
426
+ 事件分析器期望 LLM 返回 CSV section。内部会尝试自动修复格式,但如果模型输出长期不稳定,可以换用更强的模型,或降低温度。
427
+
428
+ ## License
429
+
430
+ MIT License. See [LICENSE](./LICENSE).