crawler2 / src /batch-crawler.ts
GitHub Actions Bot
🚀 Auto-deploy: 5d74de4ffce97f8bbd420c866de5e20ac52a3bad
ec4bf22
Raw
History Blame Contribute Delete
4.63 kB
import { runOttoCrawler } from './otto-crawler.js';
import { ProductData } from './productData.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
/**
* 从文件读取 URL 列表11
*/
function readUrlsFromFile(filePath: string): string[] {
try {
const content = fs.readFileSync(filePath, 'utf-8');
return content
.split('\n')
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
} catch (error: any) {
console.error(`读取文件失败: ${filePath}`, error.message);
return [];
}
}
/**
* 保存结果到 JSON 文件
*/
function saveResultsToJson(results: ProductData[], outputPath: string = './results/otto-products.json'): void {
try {
const dir = path.dirname(outputPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(outputPath, JSON.stringify(results, null, 2), 'utf-8');
console.log(`💾 结果已保存到: ${outputPath}`);
} catch (error: any) {
console.error('保存结果失败:', error.message);
}
}
/**
* 保存结果到 CSV 文件
*/
function saveResultsToCsv(results: ProductData[], outputPath: string = './results/otto-products.csv'): void {
try {
const dir = path.dirname(outputPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
if (results.length === 0) {
console.log('没有数据可保存');
return;
}
// 获取所有字段名
const fields = Object.keys(results[0]);
// 创建 CSV 内容1
const csvContent = [
fields.join(','),
...results.map(product =>
fields.map(field => {
const value = String(product[field] || '');
return `"${value.replace(/"/g, '""')}"`;
}).join(',')
)
].join('\n');
fs.writeFileSync(outputPath, csvContent, 'utf-8');
console.log(`💾 CSV 结果已保存到: ${outputPath}`);
} catch (error: any) {
console.error('保存 CSV 失败:', error.message);
}
}
/**
* 批量爬取主函数
*/
async function batchCrawl(options: {
urls?: string[];
file?: string;
preset?: string;
outputFormat?: 'json' | 'csv' | 'both';
outputFile?: string;
} = {}) {
let urls: string[] = [];
// 从文件读取或从参数获取 URLs
if (options.file) {
console.log(`📄 从文件读取 URLs: ${options.file}`);
urls = readUrlsFromFile(options.file);
} else if (options.urls && options.urls.length > 0) {
urls = options.urls;
} else {
console.error('❌ 请提供 URL 列表或文件路径');
process.exit(1);
}
if (urls.length === 0) {
console.error('❌ 没有有效的 URL');
process.exit(1);
}
console.log(`📋 待爬取 URL 数量: ${urls.length}`);
console.log(`🔧 配置预设: ${options.preset || 'stable'}`);
try {
// 执行爬取
const results = await runOttoCrawler(urls, {
preset: options.preset || 'stable'
});
console.log(`\n✅ 爬取完成!`);
console.log(`📊 成功抓取: ${results.length} 个商品\n`);
// 保存结果
const outputFormat = options.outputFormat || 'json';
const outputFile = options.outputFile || './results/otto-products';
if (outputFormat === 'json' || outputFormat === 'both') {
saveResultsToJson(results, `${outputFile}.json`);
}
if (outputFormat === 'csv' || outputFormat === 'both') {
saveResultsToCsv(results, `${outputFile}.csv`);
}
return results;
} catch (error: any) {
console.error('\n❌ 爬取失败:', error.message);
process.exit(1);
}
}
// 如果直接运行此文件
if (import.meta.url === `file://${process.argv[1]}`) {
const args = process.argv.slice(2);
const options: any = {};
// 解析命令行参数
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--file':
case '-f':
options.file = args[++i];
break;
case '--preset':
case '-p':
options.preset = args[++i];
break;
case '--format':
options.outputFormat = args[++i];
break;
case '--output':
case '-o':
options.outputFile = args[++i];
break;
default:
if (!args[i].startsWith('-')) {
if (!options.urls) options.urls = [];
options.urls.push(args[i]);
}
}
}
batchCrawl(options);
}
export { batchCrawl, readUrlsFromFile, saveResultsToJson, saveResultsToCsv };