增加角色信息的爬取工具

This commit is contained in:
yoimiya-kokomi 2022-04-08 03:39:43 +08:00
parent 3a2f626907
commit 0680636359
5 changed files with 398 additions and 6 deletions

View File

@ -18,18 +18,24 @@ export async function wiki(e, { render }) {
return false; return false;
} }
let reg = /#?(.+)(命座|天赋|技能|资料)$/, msg = e.msg; let reg = /#?(.+)(命座|命之座|天赋|技能|资料)$/, msg = e.msg;
let ret = reg.exec(msg); let ret = reg.exec(msg);
if (!ret && !ret[1]) { if (!ret && !ret[1] && !ret[2]) {
return false; return false;
} }
let char = Character.get(ret[1]); let mode = "talent";
if (/命/.test(ret[2])) {
mode = "cons";
}
let char = Character.get(ret[1]);
let base64 = await render("wiki", "character", { let base64 = await render("wiki", "character", {
save_id: "天赋" + char.name, save_id: "天赋" + char.name,
...char, ...char,
mode,
line: getLineData(char), line: getLineData(char),
_char: `/meta/character/${char.name}/` _char: `/meta/character/${char.name}/`
}); });

View File

@ -22,7 +22,7 @@ let rule = {
reg: "^#深渊(第?.{1,2}层)?(角色)?出场(率|统计)*$", reg: "^#深渊(第?.{1,2}层)?(角色)?出场(率|统计)*$",
}, },
wiki: { wiki: {
reg: "^#.*(天赋|技能|资料)$", reg: "^#.*(天赋|技能|命座|命之座|资料)$",
} }
}; };

View File

@ -39,10 +39,10 @@
</div> </div>
</div> </div>
{{if mode == "talent"}}
{{each talent skill type}} {{each talent skill type}}
<div class="talent-box"> <div class="talent-box">
<div class="talent-detail"> <div class="talent-detail">
<div class="talent-line"> <div class="talent-line">
<div class="talent-icon"> <div class="talent-icon">
@ -92,7 +92,6 @@
{{/each}} {{/each}}
<div class="talent-box"> <div class="talent-box">
<div class="talent-detail"> <div class="talent-detail">
{{each passive pass}} {{each passive pass}}
<div class="talent-line"> <div class="talent-line">
@ -109,6 +108,27 @@
{{/each}} {{/each}}
</div> </div>
</div> </div>
{{/if}}
{{if mode == "cons"}}
<div class="talent-box">
<div class="talent-detail">
{{each cons con idx}}
<div class="talent-line">
<div class="talent-icon">
<img src="{{_res_path}}{{_char}}/cons_{{idx}}.png"/>
</div>
<div class="talent-info">
<strong class="talent-name">{{con.name}}</strong>
<div class="talent-desc">
{{con.desc}}
</div>
</div>
</div>
{{/each}}
</div>
</div>
{{/if}}
<div class="logo">Create By Yunzai-Bot & Miao-Plugin</div> <div class="logo">Create By Yunzai-Bot & Miao-Plugin</div>
</div> </div>

218
tools/char-data-sprider.js Normal file
View File

@ -0,0 +1,218 @@
import fs from "fs";
import cheerio from "cheerio";
import lodash from "lodash";
import fetch from "node-fetch";
const _path = process.cwd();
let txt = fs.readFileSync(`${_path}/plugins/miao-plugin/tools/test.txt`);
function getBasic($) {
let ret = {}
// 采集基础信息
ret.name = $("#scroll_card_item").next("table").find("tr:first td:eq(1)").text();
let basic = $(".data_cont_wrapper table:first");
let title = function (title) {
return basic.find(`td:contains('${title}')`).next("td").text();
}
ret.title = title("Title");
ret.allegiance = title("Allegiance");
ret.weapon = title("Weapon Type");
ret.britydah = title("Birthday");
ret.astro = title("Astrolabe Name");
ret.cncv = title("Chinese Seiyuu");
ret.jpcv = title("Japanese Seiyuu");
ret.desc = title("In-game Description");
return ret;
}
function getStat($) {
// 采集属性信息
let stat = $("#beta_scroll_stat").next(".skilldmgwrapper").find("table");
let attrs = [], idx = 4;
stat.find("tr:first td:gt(0)").each(function (i) {
let title = $(this).text();
if (title === "Ascension") {
idx = i + 1;
console.log(idx);
return false;
}
attrs.push($(this).text())
})
let lvs = [], lvStat = {};
stat.find("tr:gt(0)").each(function (i) {
let tr = $(this), lvl = tr.find("td:first").text();
lvs.push(lvl);
let data = [];
tr.find(`td:lt(${idx})`).each(function (i) {
if (i > 0) {
data.push($(this).text())
}
})
lvStat[lvl] = data;
});
return {
lvs,
stat: attrs,
detail: lvStat
};
}
function getTalents($, eq) {
let root = $("#beta_scroll_attack_talent");
let info = root.nextAll(`.item_main_table:eq(${eq})`);
let title = info.find("tr:first td:eq(1)").text();
let icon = info.find("tr:first td:first img").attr("data-src");
// 说明
let desc = info.find("tr:eq(1) td div.skill_desc_layout").html();
desc = desc.replace(/<color=[^>]*>/g, "");
desc = desc.replace(/<\/color=[^>]*>/g, "");
desc = desc.replace(/<span class=[^>]*>/g, "<strong>");
desc = desc.replace(/<\/span>/g, "</strong>");
desc = desc.split("<br>");
lodash.forEach(desc, (txt, i) => {
desc[i] = lodash.trim(txt);
})
// detail
let detail = root.nextAll(`.skilldmgwrapper:eq(${eq})`).find("table");
let lvs = [], details = [];
detail.find("tr:first td").each(function (i) {
if (i > 0) {
lvs.push($(this).text())
}
});
detail.find("tr:gt(0)").each(function () {
let title = $(this).find("td:eq(0)").text();
let values = [], isSame = true;
$(this).find("td:gt(0)").each(function (i) {
let val = $(this).text();
values.push(val);
if (i > 0 && values[0] !== val) {
isSame = false;
}
});
details.push({
title, isSame, values
})
});
return {
title,
icon,
desc,
details,
lvs
}
}
let getPassive = function ($) {
let table = $("#beta_scroll_passive_talent").next("table")
let ret = [];
table.find("tr").each(function (idx) {
if (idx % 2 === 0) {
let ds = {};
ds.icon = $(this).find("td:first img").attr("data-src");
ds.title = $(this).find("td:eq(1)").text();
ret[idx / 2] = ds;
} else {
ret[(idx - 1) / 2].desc = $(this).find("td").text();
}
})
return ret;
}
let getCons = function ($) {
let table = $("#beta_scroll_constellation").next("table")
let ret = [];
table.find("tr").each(function (idx) {
if (idx % 2 === 0) {
let ds = {};
ds.icon = $(this).find("td:first img").attr("data-src");
ds.title = $(this).find("td:eq(1)").text();
ret[idx / 2] = ds;
} else {
ret[(idx - 1) / 2].desc = $(this).find("td").text();
}
})
return ret;
}
let getImgs = function ($) {
let cont = $("#scroll_gallery").next(".homepage_index_cont");
let img = function (idx, _cont) {
return (_cont || cont).find(`.gallery_content_cont:eq(${idx}) a`).attr("href");
}
let card = $("#scroll_name_card").nextAll(".homepage_index_cont:first");
return {
face: img(0),
side: img(1),
gacha_card: img(2),
gacha_splash: img(3),
profile: img(1, card),
party: img(2, card),
char: $("#live_beta_checker").nextAll(".item_main_table:first").find("tr:first td:first img").attr("data-src")
}
}
let getCharData = function (url, key, name = '') {
const $ = cheerio.load(txt);
let ret = getBasic($);
if (name) {
ret.name = name;
}
ret.lvStat = getStat($);
ret.talent = {
a: getTalents($, 0),
e: getTalents($, 1),
q: getTalents($, 2)
}
ret.passive = getPassive($);
ret.cons = getCons($);
ret.imgs = getImgs($);
}
async function down() {
const url = "https://genshin.honeyhunterworld.com/db/char/characters/?lang=CHS";
let req = await fetch(url);
let txt = await req.text();
let $ = cheerio.load(txt);
let char = $(".char_sea_cont:first");
char.each(function () {
let url = $(this).find("a:first").attr("href");
let keyRet = /\/char\/(\w*)\//.exec(url);
if (keyRet && keyRet[1]) {
let key = keyRet[1],
tRet = /traveler_(girl|boy)_(\w*)/.exec(key),
name;
if (tRet) {
if (tRet[1] === "girl") {
let name = { anemo: "风", geo: "岩", electro: "雷" }[tRet[2]] + "主";
getCharData(url, key, name);
} else {
return
}
}
console.log(url, key);
//let data = await getCharData(url, key, name)
}
});
}
down();

148
tools/char-init.js Normal file
View File

@ -0,0 +1,148 @@
import Data from "../components/Data.js";
import lodash from "lodash";
import { Character } from "../components/models.js";
import fs from "fs";
import { roleId, abbr } from "../../../config/genshin/roleId.js";
let roleIdMap = {};
lodash.forEach(roleId, (names, id) => {
roleIdMap[names[0]] = id;
})
let _root = process.cwd();
let characterMeta = Data.readJSON("./plugins/miao-plugin/components/meta", "characters.json");
let characters = {};
let pathName = process.cwd() + "/plugins/miao-plugin/resources/meta/character/";
// 获取指定角色的Meta信息
const getMetaData = function (name) {
if (!characterMeta[name]) {
return {};
}
const metaCfg = { lowerFirstKey: true },
meta = characterMeta[name];
// 处理基础信息
let ret = Data.getData(meta, "Name,Key,Title,desc:Description,astro:AstrolabeName", metaCfg);
ret.star = /4star/.test(meta.Star) ? 4 : 5;
let weaponid = /s_(\d*).png$/.exec(meta.Weapon);
if (weaponid) {
ret.weapon = {
233101: "长柄武器",
33101: "单手剑",
43101: "法器",
163101: "双手剑",
213101: "弓"
}[weaponid[1]]
}
// 处理图像信息
//ret.img = Data.getData(meta, "Weapon,Element,City,Profile,GachaCard,GachaSplash,Source", metaCfg);
// 处理元素
let elemRet = /([^\/]*).png$/.exec(meta.Element);
console.log(elemRet[1]);
if (elemRet && elemRet[1]) {
ret.elem = elemRet[1];
ret.element = elemName[ret.elem];
}
// 处理属性
ret.stat = Data.getData(meta, "hp:BaseHP,atk:BaseATK,def:BaseDEF,growStat:AscensionStat,growValue:AscensionStatValue", metaCfg);
ret.lvStat = lodash.map(meta.CharStat, (d) => Data.getData(d, "Name,Values", metaCfg));
if (/Mende/.test(meta.City)) {
ret.city = "蒙德"
} else if (/Liyue/.test(meta.City)) {
ret.city = "璃月";
} else if (/Daoqi/.test(meta.City)) {
ret.city = "稻妻";
}
// 处理材料
let itemKey = lodash.map("talent,boss,gemStone,Local,monster,weekly".split(","), (a) => `${a}:${lodash.upperFirst(a)}.Name`);
ret.item = Data.getData(meta, itemKey, metaCfg)
// 处理天赋
ret.talent = {
a: getTalentData(meta.NormalAttack),
e: getTalentData(meta.TalentE),
q: getTalentData(meta.TalentQ),
};
// 处理其他天赋
ret.passive = lodash.map(meta.PassiveTalents, (d) => Data.getData(d, "Name,desc:Description", metaCfg))
// 处理命座信息
let cons = {};
lodash.forEach(meta.Constellation, (data, key) => {
cons[key.replace("Constellation", "")] = Data.getData(data, "Name,desc:Description", metaCfg);
})
ret.cons = cons;
return ret;
}
// 获取Meta中的天赋信息
const getTalentData = function (data) {
let ret = Data.getData(data, "Name,desc:Description", { lowerFirstKey: true });
let attr = [], table = [], tableKeys;
lodash.forEach(data.Table, (tr) => {
let tmp = { name: tr.Name }, isTable = true, isDef = false, lastVal;
// 检查当前行是否是表格数据
lodash.forEach(tr.Values, (v) => {
// 如果为空则退出循环
if (v === "") {
isTable = false;
return false;
}
if (typeof (lastVal) === "undefined") {
// 设定初始值
lastVal = v;
} else if (lastVal != v) {
// 如果与初始值不一样,则标记退出循环
isDef = true;
return false;
}
});
if (isTable && isDef) {
if (!tableKeys) {
tableKeys = lodash.keys(tr.Values);
}
tmp.value = lodash.map(tableKeys, (k) => tr.Values[k])
table.push(tmp);
} else {
tmp.value = lastVal;
attr.push(tmp)
}
})
ret.attr = attr;
ret.table = table;
ret.tableKeys = tableKeys;
return ret;
}
lodash.forEach(characterMeta, (c) => {
let meta = Character.getMetaData(c.Name);
let data = {
id: roleIdMap[meta.name],
key: meta.key,
name: meta.name,
abbr: abbr[meta.name] || meta.name,
city: meta.city
};
lodash.defaults(data, meta)
Data.createDir(pathName, data.name)
fs.writeFileSync(`${pathName}${data.name}/data.json`, JSON.stringify(data, "", "\t"));
characters[data.id] = { id: data.id, key: data.key, name: meta.name };
})
fs.writeFileSync(`${pathName}index.json`, JSON.stringify(characters, "", "\t"));