page-analyzer
v1.2.2
Published
Standalone page analysis module.
Readme
page-analyzer
page-analyzer 是一个独立的网页分析模块,用于抓取网页、解析 DOM、提取交互元素,并结合 OpenAI 兼容的 LLM 接口生成页面区块与事件分析结果。
它适合用于:
- 分析页面上的链接、按钮、表单、输入框等可交互节点
- 将页面 DOM 元素转换为结构化 CSV,便于后续分析
- 基于视觉区块和上下文识别页面主要功能区
- 通过 LLM 判断节点可能触发的事件类型
- 为埋点设计、页面巡检、自动化测试或站点地图分析提供基础数据
功能概览
- 使用 Playwright 启动 headless Chromium 抓取真实页面内容
- 自动滚动页面并等待高度稳定,尽量覆盖懒加载内容
- 解析 HTML 中的重要元素,包括
a、button、form、input、select、textarea、标题、图片和导航区 - 收集元素文本、链接、表单动作、ARIA 信息、上下文文本、CSS 选择器和视觉区块位置
- 将交互元素导出为 CSV:
idx,blockIdx,tag,imageAlt,text,context,href - 使用 OpenAI 兼容接口进行页面区块分析和事件类型识别
- 支持紧凑输出或完整输出,便于在不同场景控制结果体积
- 支持高级用法:直接传入已抓取的 HTML、blocks、elementGeometries 进行分析
安装
从 npm 安装:
npm install page-analyzer如果在本仓库内开发:
npm installPlaywright 需要可用的 Chromium。如果运行时提示浏览器缺失,可以执行:
npx playwright install chromium环境要求
- Node.js 20.18.1 或更高版本
- 可访问目标网页的网络环境
- 一个兼容 OpenAI Chat Completions 格式的 LLM 接口
快速开始
import { analyzeUrl } from 'page-analyzer';
const result = await analyzeUrl('https://example.com', {
llm: {
apiKey: process.env.LLM_API_KEY,
apiEndpoint: process.env.LLM_API_ENDPOINT,
model: process.env.LLM_MODEL
}
});
console.log(JSON.stringify(result, null, 2));示例 .env:
LLM_API_KEY=your_api_key
LLM_API_ENDPOINT=https://api.openai.com/v1/chat/completions
LLM_MODEL=gpt-4o-mini运行测试和示例
本地测试不会调用真实网页或 LLM 接口:
npm test如需手动分析真实页面,可以运行示例脚本。它会读取项目根目录下的 .env,分析指定 URL,并把结果写入 result.json。
npm run analyze -- https://example.com注意:npm run analyze 依赖以下环境变量:
LLM_API_KEYLLM_API_ENDPOINTLLM_MODEL
缺少这些变量时会抛出配置错误。
API
analyzeUrl(url, options)
一站式分析入口。传入 URL 后,模块会自动完成页面抓取、HTML 解析、视觉区块提取、CSV 生成和 LLM 分析。
import { analyzeUrl } from 'page-analyzer';
const result = await analyzeUrl('https://example.com', {
llm: {
apiKey: 'sk-...',
apiEndpoint: 'https://api.openai.com/v1/chat/completions',
model: 'gpt-4o-mini',
temperature: 0,
maxTokens: 8000,
timeout: 600000,
maxRetries: 3
},
showEvents: true,
showBlockIdx: true,
fullPageScreenshot: true,
blockScreenshots: true,
waitForImagesLoaded: true,
knownEventTypes: ['click_link', 'submit_form'],
extractorConfig: {
viewportWidth: 1440,
viewportHeight: 900,
timeoutMs: 30000
},
parserConfig: {
contextLevel: 'full',
maxTextLength: 200
}
});参数说明:
| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| url | string | 是 | 要分析的页面 URL |
| options.llm.apiKey | string | 是 | LLM API key |
| options.llm.apiEndpoint | string | 是 | OpenAI 兼容的 Chat Completions endpoint |
| options.llm.model | string | 是 | 模型名称 |
| options.llm.temperature | number | 否 | LLM 温度,默认 0 |
| options.llm.maxTokens | number | 否 | 最大输出 token 数 |
| options.llm.timeout | number | 否 | 单次请求超时时间,默认 600000 ms |
| options.llm.maxRetries | number | 否 | LLM 请求重试次数,默认 3 |
| options.llm.interactionLogger | Function | 否 | LLM 交互日志回调 |
| options.knownEventTypes | string[] | 否 | 已知事件类型,用于判断新增事件类型 |
| options.parserConfig | object | 否 | HTML 解析配置 |
| options.extractorConfig | object | 否 | Playwright 页面抓取配置 |
| options.showEvents | boolean | 否 | 是否返回完整事件数组和元素明细 |
| options.showBlockIdx | boolean | 否 | 是否返回 CSV 与区块索引相关字段 |
| options.fullPageScreenshot | boolean | 否 | 是否保存整页截图到当前运行目录的 snapshots/ 并返回文件路径 |
| options.blockScreenshots | boolean | 否 | 是否在 LLM 合并区块后,保存每个逻辑区块截图到当前运行目录的 snapshots/ 并返回文件路径 |
| options.waitForImagesLoaded | boolean | 否 | 是否在提取区块、分析和截图前等待页面图片加载完成,默认 false |
| options.extractorConfig.s3 | object | 否 | 截图 S3 上传配置。配置后截图上传到 S3,返回 HTTPS URL;未配置时仍保存到本地 snapshots/ |
analyzePageEvents(input)
高级入口。适合你已经有 HTML、视觉区块或元素几何信息,不希望 page-analyzer 自己打开浏览器抓取页面的场景。
import { analyzePageEvents } from 'page-analyzer';
const result = await analyzePageEvents({
html: '<html><head><title>Demo</title></head><body><a href="/pricing">Pricing</a></body></html>',
url: 'https://example.com',
blocks: [],
elementGeometries: [],
llm: {
apiKey: process.env.LLM_API_KEY,
apiEndpoint: process.env.LLM_API_ENDPOINT,
model: process.env.LLM_MODEL
},
knownEventTypes: [],
parserConfig: {
contextLevel: 'lean'
},
showEvents: true,
showBlockIdx: true,
domain: 'example.com',
nodeId: 'example-root'
});主要输入:
| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| html | string | 是 | 原始 HTML |
| url | string | 是 | 页面 URL,用于解析相对链接 |
| blocks | Array | 否 | 视觉区块快照 |
| elementGeometries | Array | 否 | 页面中交互元素的几何信息 |
| llm | object | 是 | LLM 配置 |
| knownEventTypes | string[] | 否 | 已知事件类型 |
| parserConfig | object | 否 | HTML 解析配置 |
| showEvents | boolean | 否 | 是否返回完整事件数组和元素明细 |
| showBlockIdx | boolean | 否 | 是否返回 CSV 与区块索引相关字段 |
| domain | string | 否 | 分析上下文中的站点域名 |
| nodeId | string | 否 | 当前分析节点 ID |
如果没有传入 blocks,模块会根据 CSV 行构造 fallback block,保证 LLM 分析流程仍然可以运行。
输出结构
默认输出会保留页面标题、解析指标和紧凑后的区块分析结果:
{
title: 'Example Domain',
parseMetrics: {
parseMs: 12,
contextBuildMs: 5,
elementsCount: 10,
linksCount: 3,
heapUsedMB: 28.4,
contextLevel: 'full'
},
analysis: {
block_analysis: {
site_summary: '...',
blocks: [
{
blockName: '...',
blockDescription: '...',
blockSemantics: [],
blockCssPath: '...',
blockPosition: {}
}
],
stats: {
total_blocks: 4
}
}
}
}启用 showEvents: true 后,输出会包含更多用于调试和下游处理的字段,例如:
elements:解析出的页面元素明细csvContent:交互元素 CSVlinks:页面链接列表analysis.events_by_node:按节点输出的事件识别结果analysis.event_types_summary:事件类型汇总analysis.new_event_types:不在已知类型列表中的新增事件类型analysis.block_analysis.possible_event_types:页面可能存在的事件类型
启用 showBlockIdx: true 后,区块结果中会额外包含 blockIdxs、blockSemanticGroups、rowCount 等字段,并返回 csvContent。
启用 fullPageScreenshot: true 后,返回结果会包含 screenshots.fullPage,值为整页截图文件路径。
启用 blockScreenshots: true 后,模块会在 LLM 合并区块后再截图。返回结果会包含 screenshots.blocks,每项包含逻辑区块序号 blockIdx 和对应截图 path;区块分析结果中的每个 block 也会额外带上 blockScreenshotPaths,每个逻辑区块最多对应一张截图。无法通过 blockCssPath 截图的隐藏或空区块会被跳过。
如果配置 extractorConfig.s3,截图不会写入本地 snapshots/,而是直接上传到 S3;screenshots.fullPage、screenshots.blocks[].path 和 blockScreenshotPaths 会返回 HTTPS URL。S3 对象 key 使用 <prefix>/<domain>/<file-md5>.png,上传前会先检查对象是否已存在,已存在时直接返回对应 URL,避免重复上传和冗余对象。上传不会设置 ACL,访问权限沿用 bucket 策略。单张截图检查或上传失败会重试 3 次,仍失败则跳过该截图。
启用 waitForImagesLoaded: true 后,模块会先滚动页面触发懒加载,再等待当前 DOM 中的 <img> 完成加载或失败,之后再提取区块、分析和截图;等待时间受 extractorConfig.timeoutMs 控制。
截图参数启用后的新增输出示例:
{
screenshots: {
fullPage: '/path/to/page-analyzer/snapshots/example-com-20260507-095500-full-page.png',
blocks: [
{
blockIdx: 0,
path: '/path/to/page-analyzer/snapshots/example-com-20260507-095500-block-000.png'
}
]
}
}配置项
extractorConfig
extractorConfig 控制 Playwright 抓取和视觉区块提取行为。
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| timeoutMs | 30000 | 页面导航、滚动和稳定等待超时时间 |
| viewportWidth | 1440 | 浏览器视口宽度 |
| viewportHeight | 900 | 浏览器视口高度 |
| minBlockHeight | 40 | 最小区块高度 |
| minBlockWidthRatio | 0.25 | 最小区块宽度占视口宽度比例 |
| blockMaxHeightRatio | 1.5 | 最大区块高度占视口高度比例 |
| blockMaxDepth | 15 | 区块提取最大 DOM 深度 |
| textPreviewMaxChars | 1200 | 区块文本预览最大长度 |
| waitForImagesLoaded | false | 是否在提取区块、分析和截图前等待页面图片加载完成 |
| s3 | 无 | 截图 S3 上传配置。配置后截图直接上传到 S3,未配置时保存到本地 |
S3 截图上传示例:
const result = await analyzeUrl('https://example.com', {
fullPageScreenshot: true,
blockScreenshots: true,
llm: {
apiKey: process.env.LLM_API_KEY,
apiEndpoint: process.env.LLM_API_ENDPOINT,
model: process.env.LLM_MODEL
},
extractorConfig: {
s3: {
bucket: 'my-bucket',
region: 'ap-northeast-1',
prefix: 'page-analyzer/snapshots',
publicBaseUrl: 'https://cdn.example.com',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
sessionToken: process.env.AWS_SESSION_TOKEN
}
}
}
});extractorConfig.s3.bucket 和 extractorConfig.s3.region 必填。credentials 可省略,省略时使用 AWS SDK 默认凭证链。publicBaseUrl 可省略,省略时返回 https://<bucket>.s3.<region>.amazonaws.com/<key>;配置后返回 ${publicBaseUrl}/<key>。启用 S3 上传时,需要凭证具备 s3:GetObject 和 s3:PutObject 权限;如果希望不存在的对象能被稳定识别为 404,还需要对应 bucket/prefix 的 s3:ListBucket 权限。
parserConfig
parserConfig 控制 HTML 元素解析和上下文提取行为。
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| importantTags | 内置标签列表 | 要提取的重要 DOM 标签 |
| maxTextLength | 200 | 元素自身文本最大长度 |
| maxParentTextLength | 100 | 父级文本最大长度 |
| maxAncestorDepth | 5 | 祖先上下文最大深度 |
| maxNearbyTexts | 5 | 附近文本最大数量 |
| contextLevel | full | 上下文详细程度,可选 full 或 lean |
高级组件
除主 API 外,模块还导出了一些内部组件,方便按需组合:
import {
HtmlParser,
PageExtractor,
CsvExporter,
OpenAiProvider,
BaseLlmProvider,
EventAnalyzer,
assignBlocksToElements
} from 'page-analyzer';这些组件可以用于自定义分析流水线,例如只抓页面、只解析 HTML、只生成 CSV,或接入自定义 LLM provider。
LLM 接口格式
当前内置的 OpenAiProvider 会向 apiEndpoint 发送 OpenAI Chat Completions 风格请求:
{
"model": "gpt-4o-mini",
"messages": [
{
"role": "user",
"content": "..."
}
],
"temperature": 0
}响应中会读取:
data.choices[0].message.content因此,只要服务兼容这一请求和响应结构,就可以作为后端 LLM 使用。
开发
安装依赖:
npm install运行本地测试:
npm test生成 npm 发布包预览:
npm pack --dry-run发布前检查:
npm publish --dry-run项目结构
page-analyzer/
index.js # 模块入口,导出主 API 和高级组件
page-extractor.js # Playwright 页面抓取与视觉区块提取
html-parser.js # HTML 解析与元素上下文提取
csv-exporter.js # 交互元素 CSV 生成
extractors/ # 区块映射、上下文提取、选择器构建
llm/
providers/ # LLM provider
analyzers/event-analyzer/ # 页面区块和事件分析逻辑
analyzers/prompts/ # LLM prompt 模板
utils/ # 事件 CSV 解析和元数据工具
models/ # 上下文数据模型
utils/ # 文本、URL、选择器工具
vendor/ # 浏览器内区块提取脚本
scripts/analyze.js # 手动真实页面分析脚本
test/smoke.test.js # 本地 smoke test常见问题
npm run analyze 报 LLM 配置缺失
确认项目根目录存在 .env,并且包含:
LLM_API_KEY=your_api_key
LLM_API_ENDPOINT=https://api.openai.com/v1/chat/completions
LLM_MODEL=gpt-4o-miniPlaywright 报浏览器不存在
执行:
npx playwright install chromium目标页面内容不完整
可以适当调大:
extractorConfig: {
timeoutMs: 60000,
viewportHeight: 1200
}模块会自动滚动到底部并等待页面高度稳定,但如果目标站点需要登录、验证码、复杂交互或服务端限流,仍然可能拿不到完整内容。
LLM 返回格式解析失败
事件分析器期望 LLM 返回 CSV section。内部会尝试自动修复格式,但如果模型输出长期不稳定,可以换用更强的模型,或降低温度。
License
MIT License. See LICENSE.
