mirror of
https://github.com/PaiGramTeam/HonkaiStarRailWikiDataParser.git
synced 2025-01-30 10:28:39 +00:00
✨ support: some icons
This commit is contained in:
parent
0c9f1f8b86
commit
faf212844b
1
.github/workflows/python.yml
vendored
1
.github/workflows/python.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
||||
- name: Fetch Remote Files
|
||||
run: |
|
||||
python main.py
|
||||
python fix_data.py
|
||||
|
||||
- name: Commit changes
|
||||
uses: EndBug/add-and-commit@v9
|
||||
|
13
fix_data.py
Normal file
13
fix_data.py
Normal file
@ -0,0 +1,13 @@
|
||||
import asyncio
|
||||
|
||||
from res_func.avatar import fix_avatar_config
|
||||
from res_func.light_cone import fix_light_cone_config
|
||||
|
||||
|
||||
async def main():
|
||||
await fix_avatar_config()
|
||||
await fix_light_cone_config()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
3
func/README.md
Normal file
3
func/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# wiki 解析
|
||||
|
||||
通过米游社官方 wiki 解析部分数据
|
@ -16,4 +16,4 @@ headers = {
|
||||
'sec-fetch-site': 'same-site',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
|
||||
}
|
||||
client = AsyncClient(headers=headers, timeout=30)
|
||||
client = AsyncClient(headers=headers, timeout=120.0)
|
||||
|
@ -27,7 +27,6 @@ async def fetch_avatars(data: Children):
|
||||
avatar = Avatar(
|
||||
id=content.content_id,
|
||||
name=content.title,
|
||||
icon=content.icon,
|
||||
quality=m_quality,
|
||||
element=m_element,
|
||||
destiny=m_destiny,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import asyncio
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
from typing import List, Dict
|
||||
|
||||
import aiofiles
|
||||
import ujson
|
||||
@ -15,6 +15,7 @@ from models.light_cone import LightCone, LightConePromote, LightConeItem
|
||||
from models.wiki import Children
|
||||
|
||||
all_light_cones: List[LightCone] = []
|
||||
all_light_cones_name: Dict[str, LightCone] = {}
|
||||
|
||||
|
||||
async def fetch_light_cones(data: Children):
|
||||
@ -32,6 +33,7 @@ async def fetch_light_cones(data: Children):
|
||||
promote=[],
|
||||
)
|
||||
all_light_cones.append(light_cone)
|
||||
all_light_cones_name[light_cone.name] = light_cone
|
||||
|
||||
|
||||
def parse_promote(light_cone: LightCone, soup: BeautifulSoup) -> None:
|
||||
@ -109,6 +111,9 @@ async def dump_light_cones(path: Path):
|
||||
async def read_light_cones(path: Path):
|
||||
async with aiofiles.open(path, "r", encoding="utf-8") as f:
|
||||
data = ujson.loads(await f.read())
|
||||
all_light_cones.clear()
|
||||
all_light_cones_name.clear()
|
||||
for light_cone in data:
|
||||
m = LightCone(**light_cone)
|
||||
all_light_cones.append(m)
|
||||
all_light_cones_name[m.name] = m
|
||||
|
@ -44,8 +44,6 @@ class Avatar(BaseModel):
|
||||
"""角色ID"""
|
||||
name: str
|
||||
"""名称"""
|
||||
icon: str
|
||||
"""图标"""
|
||||
quality: Quality
|
||||
"""品质"""
|
||||
destiny: Destiny
|
||||
|
24
models/avatar_config.py
Normal file
24
models/avatar_config.py
Normal file
@ -0,0 +1,24 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AvatarName(BaseModel):
|
||||
Hash: int
|
||||
|
||||
|
||||
class AvatarConfig(BaseModel):
|
||||
name: str = ""
|
||||
AvatarID: int
|
||||
AvatarName: AvatarName
|
||||
AvatarVOTag: str
|
||||
Release: bool
|
||||
|
||||
|
||||
class AvatarIcon(BaseModel):
|
||||
id: int
|
||||
"""角色ID"""
|
||||
name: str
|
||||
"""名称"""
|
||||
icon: List[str]
|
||||
"""图标(从小到大)"""
|
12
models/light_cone_config.py
Normal file
12
models/light_cone_config.py
Normal file
@ -0,0 +1,12 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class LightConeIcon(BaseModel):
|
||||
id: int
|
||||
"""光锥ID"""
|
||||
name: str
|
||||
"""名称"""
|
||||
icon: List[str]
|
||||
"""图标(从小到大)"""
|
3
res_func/README.md
Normal file
3
res_func/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# 素材解析
|
||||
|
||||
通过 **** 和 https://starrailstation.com 解析图片素材
|
0
res_func/__init__.py
Normal file
0
res_func/__init__.py
Normal file
96
res_func/avatar.py
Normal file
96
res_func/avatar.py
Normal file
@ -0,0 +1,96 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
import aiofiles
|
||||
import ujson
|
||||
from bs4 import BeautifulSoup, Tag
|
||||
|
||||
from func.fetch_avatars import read_avatars, all_avatars_name, dump_avatars
|
||||
from .client import client
|
||||
from .url import avatar_config, text_map, base_station_url, avatar_url
|
||||
from models.avatar_config import AvatarConfig, AvatarIcon
|
||||
|
||||
|
||||
async def fetch_text_map() -> Dict[str, str]:
|
||||
res = await client.get(text_map)
|
||||
return res.json()
|
||||
|
||||
|
||||
async def fetch_config(text_map: Dict[str, str]) -> List[AvatarConfig]:
|
||||
res = await client.get(avatar_config)
|
||||
data = res.json()
|
||||
datas = []
|
||||
for i in data.values():
|
||||
a = AvatarConfig(**i)
|
||||
a.name = text_map[str(a.AvatarName.Hash)]
|
||||
datas.append(a)
|
||||
return datas
|
||||
|
||||
|
||||
async def parse_station(datas, tag: Tag, avatar: AvatarConfig):
|
||||
second_pic = f'{base_station_url}{tag.find("img").get("src")}'
|
||||
html = await client.get(f'{base_station_url}{tag.get("href")}')
|
||||
soup = BeautifulSoup(html.text, "lxml")
|
||||
text = soup.find("div", {"class": "a6678 a4af5"}).get("style")
|
||||
third_pic = f'{base_station_url}{text[text.find("(") + 2:text.find(")") - 1]}' if text else ""
|
||||
first_pic = f'{base_station_url}{soup.find("img", {"class": "ac39b a6602"}).get("src")}'
|
||||
datas.append(
|
||||
AvatarIcon(
|
||||
id=avatar.AvatarID,
|
||||
name=avatar.name,
|
||||
icon=[first_pic, second_pic, third_pic],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def dump_icons(path: Path, datas: List[AvatarIcon]):
|
||||
data = [icon.dict() for icon in datas]
|
||||
data.sort(key=lambda x: x["id"])
|
||||
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
||||
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
|
||||
|
||||
|
||||
async def fetch_station(configs_map: Dict[str, AvatarConfig]) -> List[AvatarIcon]:
|
||||
print("开始获取角色素材")
|
||||
html = await client.get(avatar_url)
|
||||
soup = BeautifulSoup(html.text, "lxml")
|
||||
avatars = soup.find_all("a", {"class": "char-entry-select-option"})
|
||||
tasks = []
|
||||
datas: List[AvatarIcon] = []
|
||||
ktz = False
|
||||
for avatar in avatars:
|
||||
name = avatar.find("span").get_text().strip()
|
||||
if name == "开拓者" and ktz:
|
||||
continue
|
||||
if avatar_model := configs_map.get(name):
|
||||
tasks.append(parse_station(datas, avatar, avatar_model))
|
||||
if name == "开拓者":
|
||||
ktz = True
|
||||
else:
|
||||
print(f"未找到角色 {name} 的数据")
|
||||
await asyncio.gather(*tasks)
|
||||
return datas
|
||||
|
||||
|
||||
async def fix_avatar_config():
|
||||
text_map_data = await fetch_text_map()
|
||||
configs = await fetch_config(text_map_data)
|
||||
configs_map: Dict[str, AvatarConfig] = {config.name: config for config in configs}
|
||||
configs_map["开拓者"] = configs_map["{NICKNAME}"]
|
||||
print(f"读取到原始数据:{list(configs_map.keys())}")
|
||||
data_path = Path("data")
|
||||
await read_avatars(data_path / "avatars.json")
|
||||
for key, value in all_avatars_name.items():
|
||||
if key.startswith("开拓者"):
|
||||
config = configs_map["{NICKNAME}"]
|
||||
config.name = "开拓者"
|
||||
else:
|
||||
config = configs_map.get(key)
|
||||
if config is None:
|
||||
print(f"错误:未找到角色 {key} 的配置")
|
||||
continue
|
||||
value.id = config.AvatarID
|
||||
icons = await fetch_station(configs_map)
|
||||
await dump_icons(data_path / "avatar_icons.json", icons)
|
||||
await dump_avatars(data_path / "avatars.json")
|
3
res_func/client.py
Normal file
3
res_func/client.py
Normal file
@ -0,0 +1,3 @@
|
||||
from httpx import AsyncClient
|
||||
|
||||
client = AsyncClient(timeout=120.0)
|
61
res_func/light_cone.py
Normal file
61
res_func/light_cone.py
Normal file
@ -0,0 +1,61 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Dict, List
|
||||
|
||||
import aiofiles
|
||||
import ujson
|
||||
from bs4 import BeautifulSoup, Tag
|
||||
|
||||
from func.fetch_light_cones import read_light_cones, all_light_cones_name, dump_light_cones
|
||||
from .client import client
|
||||
from .url import base_station_url, light_cone_url
|
||||
from models.light_cone_config import LightConeIcon
|
||||
|
||||
|
||||
async def parse_station(icon: LightConeIcon, tag: Tag):
|
||||
html = await client.get(f'{base_station_url}{tag.get("href")}')
|
||||
soup = BeautifulSoup(html.text, "lxml")
|
||||
first_pic = f'{base_station_url}{soup.find("img", {"class": "standard-icon a6602"}).get("src")}'
|
||||
second_pic = f'{base_station_url}{soup.find("img", {"class": "a2b16 mobile-only-elem ab8c3"}).get("src")}'
|
||||
icon.icon = [first_pic, second_pic]
|
||||
|
||||
|
||||
async def dump_icons(path: Path, datas: List[LightConeIcon]):
|
||||
data = [icon.dict() for icon in datas]
|
||||
data.sort(key=lambda x: x["id"])
|
||||
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
||||
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
|
||||
|
||||
|
||||
async def fetch_station() -> List[LightConeIcon]:
|
||||
print("开始获取光锥素材")
|
||||
html = await client.get(light_cone_url)
|
||||
soup = BeautifulSoup(html.text, "lxml")
|
||||
light_cones = soup.find_all("a", {"class": "a4041 ae026 a5abc"})
|
||||
tasks = []
|
||||
datas: List[LightConeIcon] = []
|
||||
for light_cone in light_cones:
|
||||
name = light_cone.find("span").get_text().strip()
|
||||
url = light_cone.get("href")
|
||||
nid = int(url.split("/")[-1])
|
||||
if light_cone_model := all_light_cones_name.get(name):
|
||||
light_cone_model.id = nid
|
||||
else:
|
||||
print(f"wiki 未找到光锥数据 {name} ,修复 id 失败")
|
||||
icon = LightConeIcon(
|
||||
id=nid,
|
||||
name=name,
|
||||
icon=[]
|
||||
)
|
||||
datas.append(icon)
|
||||
tasks.append(parse_station(icon, light_cone))
|
||||
await asyncio.gather(*tasks)
|
||||
return datas
|
||||
|
||||
|
||||
async def fix_light_cone_config():
|
||||
data_path = Path("data")
|
||||
await read_light_cones(data_path / "light_cones.json")
|
||||
icons = await fetch_station()
|
||||
await dump_icons(data_path / "light_cone_icons.json", icons)
|
||||
await dump_light_cones(data_path / "light_cones.json")
|
10
res_func/url.py
Normal file
10
res_func/url.py
Normal file
@ -0,0 +1,10 @@
|
||||
import base64
|
||||
|
||||
base_data_url = base64.b64decode(
|
||||
"aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0RpbWJyZWF0aC9TdGFyUmFpbERhdGEvbWFzdGVyLw=="
|
||||
).decode("utf-8")
|
||||
avatar_config = f"{base_data_url}ExcelOutput/AvatarConfig.json"
|
||||
text_map = f"{base_data_url}TextMap/TextMapCN.json"
|
||||
base_station_url = "https://starrailstation.com"
|
||||
avatar_url = f"{base_station_url}/cn/characters"
|
||||
light_cone_url = f"{base_station_url}/cn/equipment"
|
Loading…
Reference in New Issue
Block a user