本页内容
根据 OpenAI 的研究,在领域专属数据上微调 LLM,相比仅靠提示可将任务表现提升 20-40%。但瓶颈往往不在模型——而在于如何大规模获得高质量、结构化的训练数据。手动收集数据很慢。购买数据集既昂贵又常常过时。网页抓取填补了这一空白,但前提是你能提取出干净、结构化的内容,而不必把比训练模型更多的时间花在数据工程上。
CrawlForge 为 AI 训练数据流水线提供了提取层:大规模爬取域名、提取干净文本、分析内容质量,并输出可直接用于微调或生成嵌入的结构化数据集。
目录
- 为什么用网页数据训练 AI
- 架构概览
- 第 1 步:来源发现与爬取
- 第 2 步:内容提取与清洗
- 第 3 步:质量过滤与分析
- 第 4 步:为训练构建数据结构
- 第 5 步:搭建流水线
- credits 成本分析
- 成效与收益
- 常见问题
为什么用网页数据训练 AI
网络是地球上最大的领域专属文本数据库。对于专业化的 AI 应用——法律分析、医学研究、金融建模、技术文档——网页抓取往往是构建具备足够深度和时效性的训练数据集的唯一可行途径。
| 数据来源 | 成本 | 时效性 | 领域覆盖 | 体量 |
|---|---|---|---|---|
| 商业数据集 | $$$$ | 数月之前 | 有限 | 固定 |
| 内部文档 | 免费 | 当前 | 狭窄 | 小 |
| 网页抓取 | $ | 实时 | 广泛 | 无限 |
| 合成生成 | $$ | 不适用 | 可配置 | 中等 |
网页抓取拥有最佳的成本-覆盖比,但原始 HTML 并非训练数据。你需要一条能提取干净文本、过滤质量并输出结构化记录的流水线。
架构概览
这条训练数据流水线使用五个 CrawlForge 工具:
| 阶段 | 工具 | Credits | 用途 |
|---|---|---|---|
| 发现 | crawl_deep | 5 | 爬取来源域名以获取内容页面 |
| 提取 | extract_content | 2 | 从页面中提取干净、可读的文本 |
| 批量处理 | batch_scrape | 5 | 高效处理成千上万个 URL |
| 质量分析 | analyze_content | 3 | 为内容质量打分并过滤噪声 |
| 文档处理 | process_document | 3 | 解析 PDF 和文档 |
第 1 步:来源发现与爬取
首先识别并爬取你目标领域中的权威来源。
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
const client = new Client({
name: 'training-data-pipeline',
version: '1.0.0',
});
interface CrawlSource {
domain: string;
category: string;
maxPages: number;
includePatterns: string[];
excludePatterns: string[];
}
async function crawlSource(source: CrawlSource) {
const result = await client.callTool({
name: 'crawl_deep',
arguments: {
url: `https://${source.domain}`,
max_pages: source.maxPages,
max_depth: 3,
extract_content: false, // Just discover URLs first
respect_robots: true,
include_patterns: source.includePatterns,
exclude_patterns: source.excludePatterns,
},
});
const crawled = JSON.parse(result.content[0].text);
return {
domain: source.domain,
category: source.category,
urls: crawled.pages.map((p: { url: string }) => p.url),
totalDiscovered: crawled.totalPages,
};
}
// Example: Crawl technical documentation sites
const sources: CrawlSource[] = [
{
domain: 'docs.python.org',
category: 'programming',
maxPages: 500,
includePatterns: ['/3/library/', '/3/tutorial/'],
excludePatterns: ['/2.7/', '/genindex'],
},
{
domain: 'developer.mozilla.org',
category: 'web-development',
maxPages: 500,
includePatterns: ['/en-US/docs/Web/'],
excludePatterns: ['/Users/', '/search'],
},
];第 2 步:内容提取与清洗
从发现的 URL 批量提取干净文本,剥离导航、广告和样板内容。
interface ExtractedContent {
url: string;
title: string;
content: string;
wordCount: number;
category: string;
}
async function extractBatch(
urls: string[],
category: string
): Promise<ExtractedContent[]> {
const results: ExtractedContent[] = [];
// Process in batches of 25 for efficiency
const batchSize = 25;
for (let i = 0; i < urls.length; i += batchSize) {
const batch = urls.slice(i, i + batchSize);
const batchResult = await client.callTool({
name: 'batch_scrape',
arguments: {
urls: batch.map(url => ({ url })),
formats: ['text'],
includeMetadata: true,
maxConcurrency: 10,
},
});
const parsed = JSON.parse(batchResult.content[0].text);
for (const page of parsed.results) {
if (page.status === 'success' && page.content) {
const wordCount = page.content.split(/\s+/).length;
// Skip pages with too little content
if (wordCount < 200) continue;
results.push({
url: page.url,
title: page.metadata?.title || '',
content: page.content,
wordCount,
category,
});
}
}
}
return results;
}第 3 步:质量过滤与分析
并非所有网页内容都适合训练。使用 analyze_content 为质量打分并过滤掉噪声。
interface QualityScore {
url: string;
readability: number;
topicRelevance: number;
contentDepth: number;
overallScore: number;
passesFilter: boolean;
}
async function scoreContentQuality(
item: ExtractedContent,
targetTopics: string[]
): Promise<QualityScore> {
const analysis = await client.callTool({
name: 'analyze_content',
arguments: {
text: item.content,
},
});
const result = JSON.parse(analysis.content[0].text);
// Calculate topic relevance based on overlap
const detectedTopics = (result.topics || []).map(
(t: string) => t.toLowerCase()
);
const topicOverlap = targetTopics.filter(t =>
detectedTopics.some((dt: string) => dt.includes(t.toLowerCase()))
).length;
const topicRelevance = topicOverlap / targetTopics.length;
// Content depth: word count normalized (800+ words = 1.0)
const contentDepth = Math.min(item.wordCount / 800, 1.0);
// Readability: normalize to 0-1 scale
const readability = (result.readabilityScore || 50) / 100;
// Weighted overall score
const overallScore =
topicRelevance * 0.4 + contentDepth * 0.3 + readability * 0.3;
return {
url: item.url,
readability,
topicRelevance,
contentDepth,
overallScore,
passesFilter: overallScore >= 0.5, // Minimum quality threshold
};
}质量过滤通常会移除 30-50% 的爬取内容,但剩下的数据能训练出明显更好的模型。低质量数据会引入噪声,从而降低模型表现。
第 4 步:为训练构建数据结构
把过滤后的内容转换为你的训练流水线所期望的格式。
// JSONL format for fine-tuning (OpenAI compatible)
interface TrainingRecord {
messages: Array<{
role: 'system' | 'user' | 'assistant';
content: string;
}>;
metadata: {
source: string;
category: string;
quality_score: number;
};
}
function structureForFineTuning(
items: ExtractedContent[],
scores: QualityScore[]
): TrainingRecord[] {
return items
.filter((_, i) => scores[i].passesFilter)
.map((item, i) => ({
messages: [
{
role: 'system' as const,
content: `You are a knowledgeable assistant specializing in ${item.category}.`,
},
{
role: 'user' as const,
content: `Explain the following topic in detail: ${item.title}`,
},
{
role: 'assistant' as const,
content: item.content,
},
],
metadata: {
source: item.url,
category: item.category,
quality_score: scores[i].overallScore,
},
}));
}
// For embedding generation (simpler format)
interface EmbeddingRecord {
text: string;
metadata: {
source: string;
title: string;
chunk_index: number;
};
}
function structureForEmbeddings(
items: ExtractedContent[],
chunkSize: number = 512
): EmbeddingRecord[] {
const records: EmbeddingRecord[] = [];
for (const item of items) {
// Split into chunks for embedding models
const words = item.content.split(/\s+/);
const chunks = [];
for (let i = 0; i < words.length; i += chunkSize) {
chunks.push(words.slice(i, i + chunkSize).join(' '));
}
chunks.forEach((chunk, index) => {
records.push({
text: chunk,
metadata: {
source: item.url,
title: item.title,
chunk_index: index,
},
});
});
}
return records;
}第 5 步:搭建流水线
把所有阶段组合成一条完整、可复用的流水线。
async function buildTrainingDataset(
sources: CrawlSource[],
targetTopics: string[],
outputFormat: 'fine-tuning' | 'embeddings'
) {
console.log(`Starting pipeline for ${sources.length} sources...`);
let allContent: ExtractedContent[] = [];
// Stage 1: Crawl all sources
for (const source of sources) {
console.log(`Crawling ${source.domain}...`);
const crawled = await crawlSource(source);
console.log(` Found ${crawled.urls.length} pages`);
// Stage 2: Extract content
const extracted = await extractBatch(crawled.urls, crawled.category);
console.log(` Extracted ${extracted.length} quality pages`);
allContent = allContent.concat(extracted);
}
// Stage 3: Quality scoring
console.log(`\nScoring ${allContent.length} pages for quality...`);
const scores: QualityScore[] = [];
for (const item of allContent) {
scores.push(await scoreContentQuality(item, targetTopics));
}
const passing = scores.filter(s => s.passesFilter).length;
console.log(` ${passing}/${allContent.length} passed quality filter`);
// Stage 4: Structure output
if (outputFormat === 'fine-tuning') {
const records = structureForFineTuning(allContent, scores);
console.log(`\nGenerated ${records.length} training records`);
return records;
} else {
const filtered = allContent.filter((_, i) => scores[i].passesFilter);
const records = structureForEmbeddings(filtered);
console.log(`\nGenerated ${records.length} embedding chunks`);
return records;
}
}credits 成本分析
对于一个来自 5 个来源域名、共 1,000 个页面的数据集:
| 阶段 | 工具 | Credits | 数量 | 小计 |
|---|---|---|---|---|
| 爬取 | crawl_deep | 5 | 5 个域名 | 25 |
| 提取 | batch_scrape | 5 | 40 个批次 | 200 |
| 质量打分 | analyze_content | 3 | 1,000 个页面 | 3,000 |
| 文档解析 | process_document | 3 | 50 个 PDF | 150 |
| 合计 | 3,375 credits |
质量打分阶段在成本中占主导。要降低它,可在运行 analyze_content 之前按字数和 URL 模式预先过滤——这能削减 40-60% 的成本。
Professional 计划($99/月,50,000 credits)支持每月构建大型数据集。对于一次性的数据集创建,$19/月的 Hobby 计划(5,000 credits)足以覆盖一个扎实的初始数据集。
成效与收益
一条构建良好的训练数据流水线能带来:
- 规模:以小时而非数周的速度,从每个域名提取 1,000+ 个页面
- 质量:自动过滤在数据进入模型之前移除 30-50% 的噪声
- 可复现性:相同的流水线、相同的输出——没有分析师之间的差异
- 时效性:每月重新运行以保持训练数据最新
使用 CrawlForge 进行训练数据提取的团队报告称,相比手动收集,数据准备时间减少了 70-80%,并因一致的过滤而获得相当或更好的数据质量。
常见问题
为 AI 训练而进行网页抓取合法吗?
在美国,根据 hiQ Labs 诉 LinkedIn 案的判决,抓取公开数据通常是合法的。不过,你应尊重 robots.txt、服务条款和版权。CrawlForge 默认尊重 robots.txt。对于商业训练数据集,请就你所在司法管辖区的合理使用问题咨询法律顾问。
微调需要多少数据?
OpenAI 建议微调 至少 50 个样本,而有意义的提升通常从 500-1,000 个高质量样本开始显现。对于领域专属任务,2,000-5,000 个样本通常能取得出色效果。
CrawlForge 能处理 PDF 及其他文档格式吗?
可以。process_document(3 credits)可解析 PDF、DOCX 及其他格式。把它与 crawl_deep 结合以发现文档链接,然后为你的训练流水线批量处理它们。
今天就构建你的训练数据集。 免费开始,赠送 1,000 credits——足够为你的第一个数据集提取和分析 200+ 个页面。无需信用卡。
相关资源:
亲自试一试——无需注册
在 Playground 中运行 CrawlForge 的 27 个抓取与提取工具中的任意一个,然后免费开始,获取 1,000 credits。
1,000 免费 credits • 每月补充 • 无需信用卡
标签
及时获取最新洞察
将教程、产品更新与 Web 抓取技巧直接发送到你的收件箱。
拒绝垃圾邮件,随时可取消订阅。