feat: 角色下载脚本适配原神和星穹铁道
This commit is contained in:
@@ -21,7 +21,9 @@ const {
|
|||||||
* @property {string} [url] 完整 parse API 地址(与 baseUrl + wikiTitle 二选一)
|
* @property {string} [url] 完整 parse API 地址(与 baseUrl + wikiTitle 二选一)
|
||||||
* @property {string} [wikiTitle] Wiki 页面标题
|
* @property {string} [wikiTitle] Wiki 页面标题
|
||||||
* @property {string} savePath 图片保存目录
|
* @property {string} savePath 图片保存目录
|
||||||
* @property {string} imageSelector 图片 CSS 选择器
|
* @property {string} [scopeSelector] 限定搜索范围的容器选择器,如 table.fandom-table
|
||||||
|
* @property {number} [scopeIndex=0] 当存在多个 scope 时,取第几个(从 0 开始)
|
||||||
|
* @property {string} imageSelector 图片 CSS 选择器(相对于 scope 或整页)
|
||||||
* @property {string} [imageUrlAttr='src'] 图片 URL 所在属性,如 src、data-src
|
* @property {string} [imageUrlAttr='src'] 图片 URL 所在属性,如 src、data-src
|
||||||
* @property {(name: string) => string} [nameTransform] 文件名二次处理
|
* @property {(name: string) => string} [nameTransform] 文件名二次处理
|
||||||
* @property {number} [downloadDelayMs] 下载间隔,默认 500ms
|
* @property {number} [downloadDelayMs] 下载间隔,默认 500ms
|
||||||
@@ -30,6 +32,30 @@ const {
|
|||||||
|
|
||||||
/** @type {WikiConfig[]} */
|
/** @type {WikiConfig[]} */
|
||||||
const wikiConfigs = [
|
const wikiConfigs = [
|
||||||
|
{
|
||||||
|
name: 'genshin',
|
||||||
|
baseUrl: 'https://genshin-impact.fandom.com',
|
||||||
|
wikiTitle: 'Character/List',
|
||||||
|
savePath: path.join(__dirname, 'output', 'genshin'),
|
||||||
|
scopeSelector: 'table.fandom-table',
|
||||||
|
scopeIndex: 0,
|
||||||
|
imageSelector: 'td:first-child img[data-relevant="1"]',
|
||||||
|
imageUrlAttr: 'data-src',
|
||||||
|
nameTransform: (name) => name.replace(/-icon$/, ''),
|
||||||
|
verifyApiUrl: 'https://paul.ren/api/page/get?slug=photo-collection-genshin',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sr',
|
||||||
|
baseUrl: 'https://honkai-star-rail.fandom.com',
|
||||||
|
wikiTitle: 'Character/List',
|
||||||
|
savePath: path.join(__dirname, 'output', 'sr'),
|
||||||
|
scopeSelector: 'table.article-table',
|
||||||
|
scopeIndex: 0,
|
||||||
|
imageSelector: 'td:first-child img[data-relevant="1"]',
|
||||||
|
imageUrlAttr: 'data-src',
|
||||||
|
nameTransform: (name) => name.replace(/character-/, '').replace(/-icon$/, ''),
|
||||||
|
verifyApiUrl: 'https://paul.ren/api/page/get?slug=photo-collection-sr',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'nte',
|
name: 'nte',
|
||||||
baseUrl: 'https://neverness-to-everness.fandom.com/wiki/Character/List',
|
baseUrl: 'https://neverness-to-everness.fandom.com/wiki/Character/List',
|
||||||
@@ -95,12 +121,29 @@ async function fetchWikiHtml(config) {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractImages(html, imageSelector, imageUrlAttr = 'src') {
|
function extractImages(html, config) {
|
||||||
|
const {
|
||||||
|
imageSelector,
|
||||||
|
imageUrlAttr = 'src',
|
||||||
|
scopeSelector,
|
||||||
|
scopeIndex = 0,
|
||||||
|
} = config;
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
const $container = scopeSelector
|
||||||
|
? $(scopeSelector).eq(scopeIndex)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (scopeSelector && $container.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const $images = $container ? $container.find(imageSelector) : $(imageSelector);
|
||||||
const results = [];
|
const results = [];
|
||||||
const seenNames = new Set();
|
const seenNames = new Set();
|
||||||
|
|
||||||
$(imageSelector).each((_, element) => {
|
console.log('表格搜索数量:', $images.length);
|
||||||
|
|
||||||
|
$images.each((_, element) => {
|
||||||
const $img = $(element);
|
const $img = $(element);
|
||||||
const rawUrl = resolveImageUrl($img, imageUrlAttr);
|
const rawUrl = resolveImageUrl($img, imageUrlAttr);
|
||||||
|
|
||||||
@@ -119,6 +162,8 @@ function extractImages(html, imageSelector, imageUrlAttr = 'src') {
|
|||||||
results.push({ url: cleanedUrl, rawName });
|
results.push({ url: cleanedUrl, rawName });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('已去重结果数量:', results.length);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +193,7 @@ async function downloadWiki(config) {
|
|||||||
fs.mkdirSync(config.savePath, { recursive: true });
|
fs.mkdirSync(config.savePath, { recursive: true });
|
||||||
|
|
||||||
const html = await fetchWikiHtml(config);
|
const html = await fetchWikiHtml(config);
|
||||||
const images = extractImages(html, config.imageSelector, config.imageUrlAttr ?? 'src');
|
const images = extractImages(html, config);
|
||||||
|
|
||||||
console.log(`共找到 ${images.length} 张角色图`);
|
console.log(`共找到 ${images.length} 张角色图`);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ function cleanImageUrl(url) {
|
|||||||
function generateImageName(url) {
|
function generateImageName(url) {
|
||||||
const fileNameMatch = url.match(/\/([^/]+)\.png/i);
|
const fileNameMatch = url.match(/\/([^/]+)\.png/i);
|
||||||
if (fileNameMatch && fileNameMatch[1]) {
|
if (fileNameMatch && fileNameMatch[1]) {
|
||||||
return fileNameMatch[1].toLowerCase().replace(/_/g, '-');
|
// 去除 .、&、(、) 等特殊字符
|
||||||
|
return fileNameMatch[1].toLowerCase().replace(/_/g, '-').replaceAll(/\.|%26|%28|%29|%e2%80%a2/g, "").replace(/-{2,}/g, '-');
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user