File size: 4,628 Bytes
ec4bf22 ea67eaf cd3562d ea67eaf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | 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 };
|