欢迎光临
我们一直在努力

AI 辅助的竞品前端技术分析:自动抓取与技术栈对比的可视化报告

AI 辅助的竞品前端技术分析:自动抓取与技术栈对比的可视化报告

一、为何需要自动化竞品技术分析

前端团队在做技术选型时通常参考社区文章、官方 Benchmark 或自己动手试错。这些方式存在两个盲区:第一,信息滞后——社区文章的时效性难以保证;第二,缺乏横向对比——无法同时观察多个竞品的技术栈变化趋势。

自动化竞品前端技术分析的目标不是替代人工调研,而是在人工分析之前提供一个数据驱动的初始视图:这些竞品在用什么框架?构建工具链是什么?性能指标(LCP、FID、CLS)如何?CDN 回源策略是否合理?哪些第三方 SDK 是行业通用选择?

二、技术栈指纹识别引擎

竞品技术栈的识别并非简单的正则匹配。同一个技术栈在不同构建配置下可能产生完全不同的特征;反过来,不同的框架可能因为共享底层基础设施而产生相似的"指纹"。需要构建一套多特征融合的置信度评分机制。

2.1 特征采集模块

// tech-fingerprint.ts — Web 技术栈指纹识别引擎
import * as cheerio from 'cheerio';

/** 单个技术线索 */
interface TechClue {
/** 技术名称(归一化) */
name: string;
/** 检测置信度(0-1) */
confidence: number;
/** 版本号(若可检测) */
version?: string;
/** 证据来源 */
evidence: string;
}

/** 检测规则定义 */
interface DetectionRule {
name: string;
/** 检测模式类型 */
type: 'html' | 'header' | 'script' | 'global';
/** 匹配模式 */
pattern: RegExp | string;
/** 基础置信度 */
confidence: number;
}

/**
* 技术栈检测规则库
* 按检测方式分为 HTML 特征、HTTP 头特征、全局变量特征三类
*/
const DETECTION_RULES: DetectionRule[] = [
// —- React 系列 —-
{
name: 'React',
type: 'global',
pattern: /\\/\\* react-[0-9.]+ \\*\\//,
confidence: 0.9,
},
{
name: 'React',
type: 'html',
pattern: 'data-reactroot',
confidence: 0.8,
},
{
name: 'Next.js',
type: 'html',
pattern: '__NEXT_DATA__',
confidence: 0.95,
},
{
name: 'Next.js',
type: 'script',
pattern: /\\/_next\\/static\\/chunks\\//,
confidence: 0.95,
},

// —- Vue 系列 —-
{
name: 'Vue.js',
type: 'global',
pattern: /\\/\\* vue@[0-9.]+ \\*\\//,
confidence: 0.9,
},
{
name: 'Vue.js',
type: 'html',
pattern: 'data-v-',
confidence: 0.7,
},
{
name: 'Nuxt.js',
type: 'html',
pattern: '__NUXT__',
confidence: 0.95,
},

// —- 构建工具 —-
{
name: 'Webpack',
type: 'script',
pattern: /webpackJsonp/,
confidence: 0.85,
},
{
name: 'Vite',
type: 'script',
pattern: '/@vite/client',
confidence: 0.9,
},
{
name: 'esbuild',
type: 'script',
pattern: /\\/\\* esbuild-[0-9.]+ \\*\\//,
confidence: 0.8,
},

// —- CSS 方案 —-
{
name: 'Tailwind CSS',
type: 'html',
pattern: 'tailwindcss',
confidence: 0.8,
},
{
name: 'Styled Components',
type: 'html',
pattern: 'data-styled',
confidence: 0.9,
},
{
name: 'CSS Modules',
type: 'html',
pattern: /\\.module\\.css/,
confidence: 0.75,
},

// —- 监控与分析 —-
{
name: 'Google Analytics',
type: 'script',
pattern: /googletagmanager\\.com\\/gtag\\/js/,
confidence: 0.95,
},
{
name: 'Sentry',
type: 'script',
pattern: /sentry-cdn\\.com/,
confidence: 0.9,
},
{
name: 'Datadog RUM',
type: 'script',
pattern: /datadoghq\\.com\\/browser\\/rum/,
confidence: 0.9,
},

// —- CDN 方案 —-
{
name: 'Cloudflare',
type: 'header',
pattern: /cloudflare/i,
confidence: 0.9,
},
{
name: 'Vercel',
type: 'header',
pattern: /x-vercel-id/i,
confidence: 0.95,
},
];

/**
* 技术栈检测器
* 综合分析 HTML、HTTP 头、JS Bundle 特征,输出技术栈推断结果
*/
export class TechStackDetector {
private rules: DetectionRule[];

constructor(rules: DetectionRule[] = DETECTION_RULES) {
this.rules = rules;
}

/**
* 执行技术栈检测
* @param htmlContent 页面 HTML 内容
* @param headers HTTP 响应头(键值对)
* @param jsBundles JS Bundle 文件名列表
* @returns 按置信度降序排列的技术线索列表
*/
detect(
htmlContent: string,
headers: Record<string, string>,
jsBundles: string[]
): TechClue[] {
const $ = cheerio.load(htmlContent);
const clues: TechClue[] = [];

// 提取页面中所有 script 标签的 src
const scriptSrcs: string[] = [];
$('script[src]').each((_, el) => {
const src = $(el).attr('src');
if (src) scriptSrcs.push(src);
});

// 提取 HTML 中所有 meta 标签
const metaTags: Record<string, string> = {};
$('meta[name], meta[property]').each((_, el) => {
const name = $(el).attr('name') || $(el).attr('property') || '';
const content = $(el).attr('content') || '';
if (name) metaTags[name] = content;
});

for (const rule of this.rules) {
let evidence = '';
let matched = false;

switch (rule.type) {
case 'html':
matched = rule.pattern instanceof RegExp
? rule.pattern.test(htmlContent)
: htmlContent.includes(rule.pattern);
if (matched) evidence = `HTML 内容匹配: ${rule.pattern}`;
break;

case 'script':
// 同时检查 HTML 中的 script 标签和内联脚本
const allScripts = […scriptSrcs, …jsBundles];
matched = allScripts.some(s =>
rule.pattern instanceof RegExp ? rule.pattern.test(s) : s.includes(rule.pattern)
);
if (matched) evidence = `Script 引用匹配: ${rule.pattern}`;
break;

case 'header':
const headerKeys = Object.keys(headers);
matched = headerKeys.some(k =>
rule.pattern instanceof RegExp
? rule.pattern.test(k) || rule.pattern.test(headers[k])
: k.toLowerCase().includes(rule.pattern.toString().toLowerCase())
);
if (matched) evidence = `HTTP 头匹配: ${rule.pattern}`;
break;

case 'global':
// global 类型的检测在浏览器环境中通过 Puppeteer 执行
// 此处的正则仅用于标记规则,实际匹配在浏览器上下文中
matched = rule.pattern instanceof RegExp && rule.pattern.test(htmlContent);
if (matched) evidence = `全局变量/内联代码匹配`;
break;
}

if (matched) {
clues.push({
name: rule.name,
confidence: rule.confidence,
evidence,
});
}
}

// 去除重复(同一技术可能被多条规则命中,取最高置信度)
return this.deduplicate(clues);
}

/** 去重:同一技术名保留最高置信度 */
private deduplicate(clues: TechClue[]): TechClue[] {
const map = new Map<string, TechClue>();
for (const clue of clues) {
const existing = map.get(clue.name);
if (!existing || clue.confidence > existing.confidence) {
map.set(clue.name, clue);
}
}
return […map.values()].sort((a, b) => b.confidence – a.confidence);
}
}

2.2 性能数据采集

利用 Lighthouse Node API 采集竞品站点的 Core Web Vitals 数据:

// lighthouse-collector.ts — 性能指标采集模块
import lighthouse from 'lighthouse';
import * as chromeLauncher from 'chrome-launcher';

interface PerformanceReport {
url: string;
scores: {
performance: number;
accessibility: number;
bestPractices: number;
seo: number;
};
metrics: {
lcp: number; // Largest Contentful Paint (ms)
fid: number; // First Input Delay (ms)
cls: number; // Cumulative Layout Shift
tti: number; // Time to Interactive (ms)
tbt: number; // Total Blocking Time (ms)
speedIndex: number;
};
resourceSummary: {
totalRequests: number;
totalSizeKB: number;
jsSizeKB: number;
cssSizeKB: number;
imageSizeKB: number;
};
}

/**
* 执行 Lighthouse 审计
* @param url 目标 URL
* @param options 审计配置
*/
export async function runLighthouseAudit(
url: string,
options?: { chromeFlags?: string[]; onlyCategories?: string[] }
): Promise<PerformanceReport | null> {
let chrome: chromeLauncher.LaunchedChrome | undefined;

try {
// 启动无头 Chrome
chrome = await chromeLauncher.launch({
chromeFlags: options?.chromeFlags ?? [
'–headless=new',
'–no-sandbox',
'–disable-gpu',
],
});

// 执行 Lighthouse 审计
const result = await lighthouse(
url,
{
port: chrome.port,
output: 'json',
onlyCategories: options?.onlyCategories ?? ['performance'],
},
// Lighthouse 配置
{
extends: 'lighthouse:default',
settings: {
formFactor: 'desktop',
screenEmulation: { disabled: true },
throttling: {
cpuSlowdownMultiplier: 1,
requestLatencyMs: 0,
downloadThroughputKbps: 0,
uploadThroughputKbps: 0,
},
},
}
);

if (!result?.lhr) {
throw new Error('Lighthouse 未返回有效报告');
}

const { lhr } = result;

return {
url,
scores: {
performance: lhr.categories.performance?.score ?? 0,
accessibility: lhr.categories.accessibility?.score ?? 0,
bestPractices: lhr.categories['best-practices']?.score ?? 0,
seo: lhr.categories.seo?.score ?? 0,
},
metrics: {
lcp: lhr.audits['largest-contentful-paint']?.numericValue ?? 0,
fid: lhr.audits['max-potential-fid']?.numericValue ?? 0,
cls: lhr.audits['cumulative-layout-shift']?.numericValue ?? 0,
tti: lhr.audits['interactive']?.numericValue ?? 0,
tbt: lhr.audits['total-blocking-time']?.numericValue ?? 0,
speedIndex: lhr.audits['speed-index']?.numericValue ?? 0,
},
resourceSummary: {
totalRequests: lhr.audits['resource-summary']?.details?.items?.[0]?.requestCount ?? 0,
totalSizeKB: Math.round(
(lhr.audits['resource-summary']?.details?.items?.[0]?.transferSize ?? 0) / 1024
),
jsSizeKB: Math.round(
(lhr.audits['resource-summary']?.details?.items?.find(
(i: { resourceType: string }) => i.resourceType === 'script'
)?.transferSize ?? 0) / 1024
),
cssSizeKB: Math.round(
(lhr.audits['resource-summary']?.details?.items?.find(
(i: { resourceType: string }) => i.resourceType === 'stylesheet'
)?.transferSize ?? 0) / 1024
),
imageSizeKB: Math.round(
(lhr.audits['resource-summary']?.details?.items?.find(
(i: { resourceType: string }) => i.resourceType === 'image'
)?.transferSize ?? 0) / 1024
),
},
};
} catch (err) {
console.error(`[Lighthouse] 审计失败 url="${url}":`, err);
return null;
} finally {
if (chrome) {
await chrome.kill();
}
}
}

三、AI 增强:从数据到洞察

基础检测引擎可以回答"用了什么",但无法回答"为什么值得关注"。AI 模型的介入点在于对原始数据进行语义级加工:识别技术栈组合中的行业模式、对比自身技术栈与竞品的差距、生成可阅读的自然语言分析。

3.1 技术栈模式的行业对比

单次扫描可以提供快照式的结果,但持续追踪的价值更大。将多次扫描的结果按时间轴组织,可以观察到竞品技术栈的演化路径。行业中常见的技术栈迁移模式包括:

从 Webpack 向 Vite/Turbopack 的迁移,通常伴随着构建时间的显著下降和开发体验的提升;从运行时 CSS-in-JS(如 styled-components)向构建时方案(如 Tailwind CSS、UnoCSS)的切换,多数发生在产品 DAU 突破百万量级之后,核心驱动力是运行时性能开销;从自建 CDN 向 Cloudflare/Vercel Edge 的迁移,往往与全球化扩张策略同步启动。

这些模式本身并不构成决策依据,但当多个竞品在相近的时间窗口内做出类似的选择时,就需要关注——这可能意味着行业正在形成新的工程共识。

3.2 自然语言分析报告的生成

AI 模型的价值在报告生成阶段体现得尤为明显——将结构化的检测结果翻译为自然语言的洞察,供非技术决策者(如产品负责人、CTO)直接阅读。例如,将技术栈对比表格转化为以下形式的自然语言描述:

"在当前监控的 5 个竞品中,已有 3 个完成了从 Webpack 到 Vite 的迁移,平均 LCP 改善幅度为 38%。如果你方产品目前仍在使用 Webpack 5,建议在未来两个迭代内启动迁移评估。迁移的核心风险点在于 Webpack 特有 loader 的替代方案(如 raw-loader 需替换为 Vite 的 ?raw 导入语法),以及现有 require.context 的动态导入改造。"

3.3 JS Bundle 深度分析

除了 HTTP 头和 HTML 特征,JS Bundle 级别的分析能提供更细粒度的情报。通过解析竞品 Bundle 中的函数签名、模块结构和 chunk 分割策略,可以反推其代码组织方式和性能优化思路。以下通过 Puppeteer 的覆盖率分析来评估 JS 和 CSS 的未使用比例:

// bundle-coverage-analyzer.ts — JS/CSS 覆盖率分析
import puppeteer, { Browser, Page } from 'puppeteer';

interface CoverageReport {
url: string;
jsUnusedPercentage: number;
cssUnusedPercentage: number;
totalJSBytes: number;
totalCSSBytes: number;
}

/**
* 使用 Puppeteer 的 Coverage API 分析页面资源利用率
* @param url 目标页面 URL
* @param timeout 页面加载超时时间
*/
export async function analyzeBundleCoverage(
url: string,
timeout: number = 30000
): Promise<CoverageReport | null> {
let browser: Browser | null = null;

try {
browser = await puppeteer.launch({
headless: true,
args: ['–no-sandbox', '–disable-setuid-sandbox'],
});

const page: Page = await browser.newPage();

// 开启 JS 和 CSS 覆盖率采集
await Promise.all([
page.coverage.startJSCoverage(),
page.coverage.startCSSCoverage(),
]);

await page.goto(url, {
waitUntil: 'networkidle2',
timeout,
});

// 模拟用户滚动以触发懒加载
await page.evaluate(async () => {
await new Promise<void>(resolve => {
let totalScroll = 0;
const distance = 300;
const timer = setInterval(() => {
window.scrollBy(0, distance);
totalScroll += distance;
if (totalScroll >= document.body.scrollHeight || totalScroll > 5000) {
clearInterval(timer);
resolve();
}
}, 100);
});
});

// 停止采集并获取结果
const [jsCoverage, cssCoverage] = await Promise.all([
page.coverage.stopJSCoverage(),
page.coverage.stopCSSCoverage(),
]);

// 计算 JS 未使用比例
const jsStats = calculateCoverageStats(jsCoverage);
const cssStats = calculateCoverageStats(cssCoverage);

return {
url,
jsUnusedPercentage: jsStats.unusedPercentage,
cssUnusedPercentage: cssStats.unusedPercentage,
totalJSBytes: jsStats.totalBytes,
totalCSSBytes: cssStats.totalBytes,
};
} catch (err) {
console.error(`[Coverage Analyzer] 分析失败 url="${url}":`, err);
return null;
} finally {
if (browser) {
await browser.close();
}
}
}

/** 计算覆盖率统计 */
function calculateCoverageStats(
entries: Array<{ text?: string; ranges: Array<{ start: number; end: number }> }>
): { unusedPercentage: number; totalBytes: number } {
let totalBytes = 0;
let usedBytes = 0;

for (const entry of entries) {
const text = entry.text ?? '';
totalBytes += text.length;
for (const range of entry.ranges) {
usedBytes += range.end – range.start;
}
}

const unusedBytes = totalBytes – usedBytes;
const unusedPercentage = totalBytes > 0
? Math.round((unusedBytes / totalBytes) * 10000) / 100
: 0;

return { unusedPercentage, totalBytes };
}

四、报告输出示例

自动化采集完成后,生成包含 Mermaid 图表和对比表格的 Markdown 报告。以下为一个简化后的输出:

竞品前端框架构建工具CSS 方案LCP(ms)CLS监控
竞品A Next.js 14 Turbopack Tailwind CSS 1200 0.02 Datadog
竞品B React 18 + Vite Vite 5 CSS Modules 1800 0.08 Sentry
竞品C Vue 3 + Nuxt 3 Vite 5 UnoCSS 980 0.01 自研
自身 React 18 + Webpack Webpack 5 Styled Components 2400 0.15 无

从报告可以直观看出:自身的技术栈在构建工具(Webpack vs Vite)和 CSS 方案(运行时 CSS-in-JS vs 构建时方案)上存在性能差距,LCP 高出竞品中位数约 60%。

五、总结

AI 辅助的竞品前端技术分析将"人工点开 F12 一个个看"的低效过程自动化为可重复的工程流程。其核心价值不在于输出一份静态报告,而在于建立持续追踪的长效机制——当竞品进行了技术栈升级时(如从 Webpack 迁移至 Turbopack),能够在下一轮自动扫描中被检测并标注。

需要说明的是,技术栈检测是概率性的。某些情况下(如 SSR + CSR 混合、自定义构建流水线),单一规则可能无法准确识别。在生成最终报告前设置置信度阈值(建议 ≥0.7),低于阈值的结果标记为"疑似"而非"确定",避免错误信息影响决策。

赞(0)
未经允许不得转载:171主机测评 » AI 辅助的竞品前端技术分析:自动抓取与技术栈对比的可视化报告
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址