On this page
The AI revolution runs on data. Whether you're fine-tuning LLMs, building RAG systems, or training custom models, web data is often your richest source of training material.
But collecting high-quality training data from the web isn't straightforward. This guide covers everything: ethical considerations, collection pipelines, quality assurance, and practical implementation with CrawlForge.
The Data Bottleneck
AI models are only as good as their training data. Yet most teams face critical challenges:
- Quantity: Models need millions of examples
- Quality: Garbage in, garbage out
- Diversity: Training on narrow data creates narrow models
- Freshness: Static datasets become stale
- Compliance: Legal and ethical considerations
Web scraping solves the quantity and diversity problems—but only if done right.
Types of Web Data for AI
Text Content
The most common training data type:
- Articles and blog posts - Narrative text for language understanding
- Documentation - Technical writing and structured explanations
- Forums and Q&A - Conversational patterns and problem-solving
- Product descriptions - Concise, descriptive text
- Reviews - Sentiment-rich content with opinions
Structured Data
For classification and entity recognition:
- Product catalogs - Items with attributes
- Business listings - Entities with relationships
- Event data - Temporal and location information
- Tables and datasets - Numerical and categorical data
Metadata
Often overlooked but valuable:
- SEO tags - Human-written summaries and keywords
- Schema.org markup - Structured entity data
- Social graphs - Relationship data
- Timestamps - Temporal patterns
Multi-Modal
For vision and multi-modal models:
- Images with captions - Visual-language pairs
- PDFs with text - Document understanding
- Videos with transcripts - Temporal visual-language
Ethical Web Scraping Principles
Before collecting data, understand the ethical and legal landscape.
1. Respect robots.txt
robots.txt tells crawlers what's allowed:
# Example robots.txt
User-agent: *
Disallow: /private/
Disallow: /api/
Allow: /public/
Crawl-delay: 10
CrawlForge respects robots.txt by default. You can check any site's policy:
curl https://example.com/robots.txt2. Rate Limiting
Don't overwhelm servers:
- Respect Crawl-delay directives
- Space requests 1-5 seconds apart minimum
- Monitor response codes - 429 means slow down
- Reduce concurrency for smaller sites
CrawlForge has built-in rate limiting, but be respectful.
3. Data Licensing
Understand content rights:
- Creative Commons - Usually fine with attribution
- Copyright - Requires permission for training
- Terms of Service - Some sites prohibit scraping
- GDPR/Privacy - Personal data has restrictions
4. The LLMs.txt Standard
A new standard for AI-specific permissions:
# llms.txt example
Allow: training
Allow: inference
Require: attribution
Contact: ai@example.com
Inspect each site's llms.txt (or robots.txt) to discover AI permissions before crawling.
Building a Data Collection Pipeline
Architecture Overview
┌──────────────────────────────────────────────────┐
│ 1. Source Discovery │
│ - Identify target websites │
│ - Map site structure │
│ - Prioritize high-quality sources │
└──────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────┐
│ 2. Content Extraction │
│ - Fetch pages │
│ - Extract main content │
│ - Handle pagination │
└──────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────┐
│ 3. Data Cleaning │
│ - Remove duplicates │
│ - Filter low-quality content │
│ - Normalize formats │
└──────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────┐
│ 4. Quality Validation │
│ - Language detection │
│ - Content scoring │
│ - Deduplication │
└──────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────┐
│ 5. Storage & Export │
│ - Format for training │
│ - Version control │
│ - Documentation │
└──────────────────────────────────────────────────┘
Step 1: Source Discovery
Start by mapping available content:
// Map a documentation site
const response = await fetch('https://crawlforge.dev/api/v1/tools/map_site', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://docs.example.com',
maxDepth: 3,
includePatterns: ['/docs/*', '/guides/*'],
excludePatterns: ['/api/*', '/admin/*']
})
});
const { data } = await response.json();
console.log(`Found ${data.urls.length} pages to scrape`);
// Found 847 pages to scrapeCost: 2 credits per map_site call
Step 2: Content Extraction
Extract content from discovered URLs:
// Batch scrape all discovered URLs
const urls = data.urls;
// Process in batches of 50
for (let i = 0; i < urls.length; i += 50) {
const batch = urls.slice(i, i + 50);
const response = await fetch('https://crawlforge.dev/api/v1/tools/batch_scrape', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
urls: batch,
extractContent: true,
includeMetadata: true
})
});
const { data } = await response.json();
// Store results
for (const result of data.results) {
await storeDocument({
url: result.url,
content: result.content,
metadata: result.metadata,
scrapedAt: new Date()
});
}
// Respect rate limits
await sleep(1000);
}Cost: 5 credits per batch_scrape call (up to 50 URLs)
Step 3: Data Cleaning
Remove noise and normalize content:
function cleanDocument(doc: ScrapedDocument): CleanedDocument {
let content = doc.content;
// Remove boilerplate
content = removeNavigation(content);
content = removeFooters(content);
content = removeAds(content);
// Normalize whitespace
content = content.replace(/\s+/g, ' ').trim();
// Remove very short documents
if (content.split(' ').length < 50) {
return null; // Too short
}
// Remove documents with too much code
const codeRatio = countCodeBlocks(content) / content.length;
if (codeRatio > 0.8) {
return null; // Mostly code, not useful for text training
}
return {
...doc,
content,
wordCount: content.split(' ').length,
cleanedAt: new Date()
};
}Step 4: Quality Validation
Use AI to score content quality:
// Analyze content quality
const response = await fetch('https://crawlforge.dev/api/v1/tools/analyze_content', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: doc.content
})
});
const { data } = await response.json();
// Filter based on analysis
if (data.language !== 'en') {
// Skip non-English content (or separate by language)
}
if (data.readability.score < 30) {
// Skip content that's too technical/unreadable
}
if (data.sentiment.toxic > 0.5) {
// Skip potentially harmful content
}Cost: 3 credits per analyze_content call
Step 5: Deduplication
Remove near-duplicates:
import { simhash } from './hashing';
const seen = new Map<string, string>(); // hash -> url
function isDuplicate(doc: CleanedDocument): boolean {
const hash = simhash(doc.content);
for (const [existingHash, existingUrl] of seen) {
const similarity = hammingDistance(hash, existingHash);
if (similarity > 0.95) {
console.log(`Duplicate: ${doc.url} ~= ${existingUrl}`);
return true;
}
}
seen.set(hash, doc.url);
return false;
}Data Quality for LLM Training
Quality Metrics to Track
| Metric | Target | Why It Matters |
|---|---|---|
| Unique documents | >95% | Avoids memorization |
| Avg word count | 200-5000 | Balanced context lengths |
| Language purity | >99% | Consistent training signal |
| Readability score | 40-80 | Human-quality text |
| Freshness | <1 year | Current information |
Format for Training
For LLM fine-tuning, output JSONL:
{"text": "Document content here...", "source": "docs.example.com", "category": "tutorial"}
{"text": "Another document...", "source": "blog.example.com", "category": "article"}For RAG systems, include embeddings:
{"text": "...", "embedding": [0.123, 0.456, ...], "metadata": {"url": "...", "title": "..."}}Scaling Your Pipeline
Credit Optimization
For large-scale collection:
- Start with map_site (2 credits) to discover URLs
- Use batch_scrape (5 credits/50 URLs) instead of individual calls
- Skip analyze_content for known-good sources
- Cache aggressively - same URL = same content
Estimated Costs
| Dataset Size | Tools | Credits | Cost (Pro Plan) |
|---|---|---|---|
| 1K docs | map + batch | ~500 | $1 |
| 10K docs | map + batch | ~2,500 | $5 |
| 100K docs | map + batch | ~15,000 | $30 |
| 1M docs | map + batch + analysis | ~100,000 | $200 |
Incremental Updates
Don't re-scrape everything:
// Check for changes before re-scraping
const response = await fetch('https://crawlforge.dev/api/v1/tools/track_changes', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: doc.url,
lastHash: doc.contentHash
})
});
const { data } = await response.json();
if (data.hasChanges) {
// Re-scrape and update
} else {
// Skip, content unchanged
}Cost: 3 credits per track_changes call
Common Pitfalls
1. Over-Scraping
Problem: Collecting too much low-quality data Solution: Quality > quantity. 100K good documents beat 1M mediocre ones.
2. Ignoring Data Quality
Problem: Training on noisy data Solution: Invest in cleaning and validation. Use analyze_content.
3. Copyright Violations
Problem: Using copyrighted content without permission Solution: Stick to permissive sources. Check robots.txt and ToS.
4. Rate Limit Exhaustion
Problem: Getting blocked by target sites Solution: Use stealth_mode (5 credits) for sensitive sites. Respect crawl delays.
5. Stale Data
Problem: Training on outdated information Solution: Set up recurring scrapes with track_changes.
Case Study: Building a Documentation Dataset
Goal: Create a training dataset from technical documentation for a code assistant.
Sources
- Official framework docs (React, Vue, Next.js, etc.)
- API references
- Tutorial sites with permissive licenses
Pipeline
const SOURCES = [
'https://react.dev/learn',
'https://vuejs.org/guide',
'https://nextjs.org/docs',
// ... more sources
];
async function buildDataset() {
const documents = [];
for (const source of SOURCES) {
// 1. Map site structure
const siteMap = await mapSite(source);
// 2. Filter to documentation pages
const docUrls = siteMap.urls.filter(url =>
url.includes('/docs') ||
url.includes('/guide') ||
url.includes('/learn')
);
// 3. Batch scrape
const scraped = await batchScrape(docUrls);
// 4. Clean and validate
for (const doc of scraped) {
const cleaned = cleanDocument(doc);
if (cleaned && !isDuplicate(cleaned)) {
documents.push(cleaned);
}
}
}
// 5. Export
await exportToJSONL(documents, 'training_data.jsonl');
console.log(`Dataset: ${documents.length} documents`);
}Results
- Sources: 12 documentation sites
- Pages scraped: 15,847
- After cleaning: 12,392 documents
- After deduplication: 11,108 unique documents
- Total words: 8.2M
- Credits used: ~4,500
- Cost: ~$9 (Professional plan)
Conclusion
Web scraping for AI training data is a balance of quantity, quality, and ethics. The key principles:
- Start with clear goals - What does your model need?
- Prioritize quality - Clean data beats more data
- Respect sources - Follow robots.txt and rate limits
- Validate thoroughly - Use automated quality checks
- Iterate continuously - Models improve with better data
CrawlForge provides the tools to build production-grade data pipelines. Start with 1,000 free credits at crawlforge.dev/signup.
Resources:
- API Reference - Full tool documentation
- Batch Processing Guide - Large-scale scraping
- Credit Optimization - Reduce costs
Try this yourself — no signup needed
Run any of CrawlForge's 27 scraping and extraction tools in the playground, then start free with 1,000 credits.
1,000 free credits • Refills monthly • No credit card required
Tags
About the Author
Stay updated with the latest insights
Get tutorials, product updates, and web scraping tips delivered to your inbox.
No spam. Unsubscribe anytime.