中级指南
批量 处理指南
通过高效的队列管理、错误恢复和性能优化策略,将网页抓取扩展到数千个 URL。
1. 使用 batch_scrape 工具
batch_scrape 工具在单次同步请求中最多并发抓取 50 个 URL。每个 URL 都会返回各自的状态,因此单个失效页面不会拖垮整批任务。
基础批量抓取
每个尝试的 URL 5 credits(50 个 URL = 250 credits)
Bash
curl -X POST https://crawlforge.dev/api/v1/tools/batch_scrape \
-H "X-API-Key: cf_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": [
{ "url": "https://example.com/page1" },
{ "url": "https://example.com/page2" },
{ "url": "https://example.com/page3" }
],
"batch_config": { "concurrency": 5 }
}'REST API 没有异步模式: 通过 HTTP 调用的
batch_scrape 是同步的——一次请求进入,完成的结果返回。没有可轮询的作业 id,也没有 webhook:batch_id 是 get_batch_results 的检索键,而不是作业句柄。超过 50 个 URL 时请按下文的方式分块。CrawlForge MCP 服务器则可以用 mode: 'async' 和可选的 webhook 在后台运行同样的 50 URL 批次。2. 队列管理
通过将数千个 URL 切分成多个批次并管理队列来处理它们。
切分策略
将大型 URL 列表拆分成可管理的批次
Typescript
// Chunk array into batches of 50
function chunkArray<T>(array: T[], size: number): T[][] {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
// Process all URLs in batches
async function processBatches(urls: string[]) {
const batches = chunkArray(urls, 50); // Max 50 URLs per batch
const allResults: any[] = [];
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
console.log(`Processing batch ${i + 1}/${batches.length}...`);
const response = await fetch('https://crawlforge.dev/api/v1/tools/batch_scrape', {
method: 'POST',
headers: {
'X-API-Key': process.env.CRAWLFORGE_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
urls: batch.map(url => ({ url })),
batch_config: { concurrency: 8 },
}),
});
const data = await response.json();
allResults.push(...data.data.results);
// Wait between batches to respect rate limits
if (i < batches.length - 1) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
return allResults;
}
// Usage
const urls = [...]; // 500 URLs
const results = await processBatches(urls);
console.log(`Scraped ${results.length} total pages`);专业提示: 使用 Redis 或数据库来存储你的队列。这样在脚本崩溃或需要重启时,你可以恢复处理。
3. 错误恢复
通过重试逻辑和错误追踪优雅地处理失败。
健壮的错误处理
Typescript
interface BatchResult {
successful: any[];
failed: { url: string; error: string }[];
}
async function batchScrapeWithRetry(
urls: string[],
maxRetries = 3
): Promise<BatchResult> {
const successful: any[] = [];
const failed: { url: string; error: string }[] = [];
let remainingUrls = [...urls];
let retries = 0;
while (remainingUrls.length > 0 && retries <= maxRetries) {
try {
const response = await fetch('https://crawlforge.dev/api/v1/tools/batch_scrape', {
method: 'POST',
headers: {
'X-API-Key': process.env.CRAWLFORGE_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
urls: remainingUrls.map(url => ({ url })),
batch_config: { concurrency: 5 },
}),
});
const data = await response.json();
// Each result carries its own status: 'success' | 'failed' | 'skipped'.
// 'skipped' means the request's time budget ran out before that URL was
// started — those are not charged, so they are always worth retrying.
const batchSuccessful = data.data.results.filter((r: any) => r.status === 'success');
const batchUnfinished = data.data.results.filter((r: any) => r.status !== 'success');
successful.push(...batchSuccessful);
// Only retry the URLs that did not come back successful
remainingUrls = batchUnfinished.map((r: any) => r.url);
if (remainingUrls.length > 0) {
console.log(`Retrying ${remainingUrls.length} failed URLs...`);
retries++;
await new Promise(resolve => setTimeout(resolve, 2000 * retries));
}
} catch (error) {
console.error('Batch request failed:', error);
retries++;
if (retries > maxRetries) {
// Mark all remaining URLs as failed
failed.push(...remainingUrls.map(url => ({
url,
error: String(error)
})));
break;
}
await new Promise(resolve => setTimeout(resolve, 2000 * retries));
}
}
return { successful, failed };
}
// Usage
const { successful, failed } = await batchScrapeWithRetry(urls);
console.log(`Success: ${successful.length}, Failed: ${failed.length}`);
// Save failed URLs for manual review
if (failed.length > 0) {
fs.writeFileSync('failed-urls.json', JSON.stringify(failed, null, 2));
}4. 性能优化
通过这些优化策略,最大化吞吐量并最小化成本。
优化并发数
从
batch_config.concurrency: 5 开始。该 API 接受 1-10,实际最多运行 8 个工作线程注意时间预算
单次请求的抓取时间约为 20 秒。未能开始的 URL 会返回
skipped,而被跳过的 URL 不计费重新读取,而不是重新抓取
结果会保存 24 小时。请用
get_batch_results(1 credit)翻页读取,而不是重跑整批(每个 URL 5 credits)缓存结果
将抓取的数据存储在 Redis/数据库中,避免重复抓取相同的 URL
避免过度批处理
每批次不要超过 50 个 URL——应拆分成多个请求
不要忽视速率限制
遵守你所在套餐的速率限制(Free:1/s,Hobby:2/s,Pro:4/s,Business:10/s)
预期性能
| 场景 | 时间 | 设置 |
|---|---|---|
| 小批量(10 个 URL) | 约 5 秒 | concurrency: 5 |
| 中批量(50 个 URL) | 约 15 秒 | concurrency: 8 |
| 大批量(500 个 URL) | 约 3 分钟 | 10 批 × 50 个 URL |
| 超大批量(5,000 个 URL) | 约 30 分钟 | 100 批 × 50 个 URL |
下一步
继续学习更多高级指南