fix: 修复式神抓取脚本缺失L和G类型的问题

- 添加 type=ld (L类型) 和 type=gt (G类型) 的抓取支持
- 将 RARITY_TYPES 改为对象数组,支持 URL 参数和目录名映射
- 修复图片保存路径,确保 L 和 G 类型图片正确保存到对应目录
- 现在可以完整抓取所有 8 种稀有度类型的式神数据 (SSR/SP/UR/SR/R/N/L/G)
This commit is contained in:
2026-02-11 18:38:20 +08:00
parent 6b722cf8e0
commit ba264981c3

View File

@@ -0,0 +1,200 @@
const puppeteer = require('puppeteer');
const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');
const IMAGE_DIR = path.join(__dirname, '../public/assets/Shikigami');
const JSON_FILE = path.join(__dirname, '../src/data/Shikigami.json');
// Rarity types mapping: URL parameter -> directory name
const RARITY_TYPES = [
{ urlParam: 'ssr', dir: 'ssr' },
{ urlParam: 'sp', dir: 'sp' },
{ urlParam: 'ur', dir: 'ur' },
{ urlParam: 'sr', dir: 'sr' },
{ urlParam: 'r', dir: 'r' },
{ urlParam: 'n', dir: 'n' },
{ urlParam: 'ld', dir: 'l' },
{ urlParam: 'gt', dir: 'g' }
];
// Ensure image directory exists
function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
}
// Download image
function downloadImage(url, filepath) {
return new Promise((resolve, reject) => {
const protocol = url.startsWith('https') ? https : http;
const file = fs.createWriteStream(filepath);
protocol.get(url, (response) => {
if (response.statusCode === 200) {
response.pipe(file);
file.on('finish', () => {
file.close();
resolve();
});
} else if (response.statusCode === 302 || response.statusCode === 301) {
downloadImage(response.headers.location, filepath).then(resolve).catch(reject);
} else {
reject(new Error(`Failed: ${response.statusCode}`));
}
}).on('error', (err) => {
fs.unlink(filepath, () => {});
reject(err);
});
});
}
// Main function
async function main() {
let browser;
try {
console.log('Starting Shikigami scraper with Puppeteer...\n');
// Launch browser
console.log('Launching browser...');
browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
const allShikigami = [];
const seenIds = new Set();
// Scrape each rarity type
for (const rarity of RARITY_TYPES) {
console.log(`Fetching ${rarity.dir.toUpperCase()} Shikigami (type=${rarity.urlParam})...`);
const url = `https://yys.163.com/shishen/index.html?type=${rarity.urlParam}`;
try {
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
// Wait for content to load
await page.waitForSelector('.shishen_item', { timeout: 10000 });
// Extract data from page
const shikigami = await page.evaluate((rarityDir) => {
const items = document.querySelectorAll('.shishen_item');
const results = [];
items.forEach(item => {
const link = item.querySelector('a');
const img = item.querySelector('img');
const nameSpan = item.querySelector('.name');
if (link && img && nameSpan) {
const href = link.getAttribute('href');
const imgSrc = img.getAttribute('src');
const name = nameSpan.textContent.trim();
const id = href.replace('.html', '');
results.push({
id,
name,
imgSrc,
rarity: rarityDir.toUpperCase()
});
}
});
return results;
}, rarity.dir);
// Add only unique shikigami (avoid duplicates)
shikigami.forEach(s => {
if (!seenIds.has(s.id)) {
seenIds.add(s.id);
allShikigami.push(s);
}
});
console.log(`Found ${shikigami.length} ${rarity.dir.toUpperCase()} Shikigami (${seenIds.size} unique total)\n`);
// Small delay
await new Promise(resolve => setTimeout(resolve, 500));
} catch (err) {
console.error(`Error fetching ${rarity}:`, err.message);
}
}
await browser.close();
console.log(`\nTotal unique Shikigami found: ${allShikigami.length}`);
if (allShikigami.length === 0) {
console.log('\n⚠ No Shikigami found. Please check the website manually.');
return;
}
// Sort by ID descending (newest first)
allShikigami.sort((a, b) => parseInt(b.id) - parseInt(a.id));
console.log(`Sorted by ID (newest first): ${allShikigami[0].id} -> ${allShikigami[allShikigami.length - 1].id}`);
console.log('\nDownloading images...\n');
// Ensure directories exist
RARITY_TYPES.forEach(rarity => {
ensureDir(path.join(IMAGE_DIR, rarity.dir));
});
// Download images with progress
let downloaded = 0;
const total = allShikigami.length;
for (let i = 0; i < allShikigami.length; i++) {
const shikigami = allShikigami[i];
const { id, imgSrc, rarity, name } = shikigami;
const filename = `${id}.png`;
const filepath = path.join(IMAGE_DIR, rarity.toLowerCase(), filename);
// Skip if file already exists
if (fs.existsSync(filepath)) {
downloaded++;
process.stdout.write(`\rProgress: ${downloaded}/${total} (${name}) - skipped`);
continue;
}
try {
await downloadImage(imgSrc, filepath);
downloaded++;
process.stdout.write(`\rProgress: ${downloaded}/${total} (${name})`);
} catch (err) {
console.error(`\n✗ Failed ${id} (${name}): ${err.message}`);
}
}
console.log(`\n\nDownloaded ${downloaded}/${total} images`);
// Update JSON file
console.log('\nUpdating JSON configuration...');
const jsonData = allShikigami.map(s => ({
avatar: `/assets/Shikigami/${s.rarity.toLowerCase()}/${s.id}.png`,
name: s.name,
rarity: s.rarity
}));
fs.writeFileSync(JSON_FILE, JSON.stringify(jsonData, null, 4), 'utf8');
console.log(`✓ Updated ${JSON_FILE}`);
console.log('\n✓ Scraping completed successfully!');
console.log(`Total: ${allShikigami.length} unique Shikigami`);
console.log(`Sorted by ID descending (newest first)`);
} catch (error) {
console.error('Error:', error);
if (browser) await browser.close();
process.exit(1);
}
}
main();