mirror of
https://github.com/PaiGramTeam/HonkaiStarRailWikiDataParser.git
synced 2024-11-16 14:31:24 +00:00
✨ support: avatars
This commit is contained in:
parent
24708edf2b
commit
30ba8afa3b
128
func/fetch_avatars.py
Normal file
128
func/fetch_avatars.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
import aiofiles
|
||||||
|
import ujson
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from func.client import client
|
||||||
|
from func.url import info_url
|
||||||
|
from func.fetch_materials import all_materials_map, all_materials_name
|
||||||
|
from models.enums import Quality, Element, Destiny
|
||||||
|
from models.avatar import Avatar, AvatarInfo, AvatarSoul, AvatarItem, AvatarPromote
|
||||||
|
from models.wiki import Children
|
||||||
|
|
||||||
|
all_avatars: List[Avatar] = []
|
||||||
|
all_avatars_map: Dict[int, Avatar] = {}
|
||||||
|
all_avatars_name: Dict[str, Avatar] = {}
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_avatars(data: Children):
|
||||||
|
for content in data.list:
|
||||||
|
m_element = Element(re.findall(r'属性/(.*?)\\', content.ext)[0])
|
||||||
|
m_destiny = Destiny(re.findall(r'命途/(.*?)\\', content.ext)[0])
|
||||||
|
m_quality = Quality(re.findall(r'星级/(.*?)\\', content.ext)[0])
|
||||||
|
avatar = Avatar(
|
||||||
|
id=content.content_id,
|
||||||
|
name=content.title,
|
||||||
|
quality=m_quality,
|
||||||
|
element=m_element,
|
||||||
|
destiny=m_destiny,
|
||||||
|
information=AvatarInfo(),
|
||||||
|
promote=[],
|
||||||
|
soul=[],
|
||||||
|
)
|
||||||
|
all_avatars.append(avatar)
|
||||||
|
all_avatars_map[avatar.id] = avatar
|
||||||
|
all_avatars_name[avatar.name] = avatar
|
||||||
|
|
||||||
|
|
||||||
|
def parse_promote(avatar: Avatar, soup: BeautifulSoup) -> None:
|
||||||
|
"""解析角色突破数据"""
|
||||||
|
lis = soup.find_all("li", {"class": "obc-tmpl__switch-item"})
|
||||||
|
required_levels = [0, 20, 30, 40, 50, 60, 70, 80]
|
||||||
|
max_level = [0, 30, 40, 50, 60, 70, 80, 90]
|
||||||
|
for i in range(1, 8):
|
||||||
|
promote = AvatarPromote(
|
||||||
|
required_level=required_levels[i],
|
||||||
|
max_level=max_level[i],
|
||||||
|
items=[],
|
||||||
|
)
|
||||||
|
materials = lis[i].find_all("li", {"data-target": "breach.attr.material"})
|
||||||
|
for material in materials:
|
||||||
|
try:
|
||||||
|
mid = int(re.findall(r"content/(\d+)/detail", material.find("a").get("href"))[0])
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
name = material.find("span", {"class": "obc-tmpl__icon-text"}).text
|
||||||
|
item = all_materials_map.get(mid)
|
||||||
|
if not item:
|
||||||
|
item = all_materials_name.get(name)
|
||||||
|
try:
|
||||||
|
count = int(material.find("span", {"class": "obc-tmpl__icon-num"}).text.replace("*", ""))
|
||||||
|
except AttributeError:
|
||||||
|
count = 1
|
||||||
|
if name == "信用点":
|
||||||
|
promote.coin = count
|
||||||
|
elif item:
|
||||||
|
promote.items.append(
|
||||||
|
AvatarItem(
|
||||||
|
item=item,
|
||||||
|
count=count,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"unknown material: {mid}: {name}")
|
||||||
|
avatar.promote.append(promote)
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_info(avatar: Avatar):
|
||||||
|
print(f"Fetch avatar info: {avatar.id}: {avatar.name}")
|
||||||
|
params = {
|
||||||
|
'app_sn': 'sr_wiki',
|
||||||
|
'content_id': str(avatar.id),
|
||||||
|
}
|
||||||
|
resp = await client.get(info_url, params=params)
|
||||||
|
data = resp.json()["data"]["content"]["contents"][0]["text"]
|
||||||
|
soup = BeautifulSoup(data, "lxml")
|
||||||
|
items = soup.find_all("div", {"class": "obc-tmp-character__item"})
|
||||||
|
avatar.information.faction = items[2].find("div", {"class": "obc-tmp-character__value"}).text
|
||||||
|
avatar.information.occupation = items[3].find("div", {"class": "obc-tmp-character__value"}).text
|
||||||
|
parse_promote(avatar, soup)
|
||||||
|
# 星魂
|
||||||
|
table = soup.find_all("table")[-1]
|
||||||
|
trs = table.find_all("tr")[1:]
|
||||||
|
for tr in trs:
|
||||||
|
ps = tr.find_all("p")
|
||||||
|
desc = ps[2].text.strip() if len(ps) > 2 else ps[1].text.strip()
|
||||||
|
avatar.soul.append(
|
||||||
|
AvatarSoul(
|
||||||
|
name=ps[0].text.strip(),
|
||||||
|
desc=desc,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def fetch_avatars_infos():
|
||||||
|
tasks = []
|
||||||
|
for avatar in all_avatars:
|
||||||
|
tasks.append(fetch_info(avatar))
|
||||||
|
await asyncio.gather(*tasks)
|
||||||
|
|
||||||
|
|
||||||
|
async def dump_avatars(path: Path):
|
||||||
|
data = [avatar.dict() for avatar in all_avatars]
|
||||||
|
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
||||||
|
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
|
async def read_avatars(path: Path):
|
||||||
|
async with aiofiles.open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = ujson.loads(await f.read())
|
||||||
|
for avatar in data:
|
||||||
|
m = Avatar(**avatar)
|
||||||
|
all_avatars.append(m)
|
||||||
|
all_avatars_map[m.id] = m
|
||||||
|
all_avatars_name[m.name] = m
|
@ -22,6 +22,7 @@ star_map = {
|
|||||||
}
|
}
|
||||||
all_materials: List[Material] = []
|
all_materials: List[Material] = []
|
||||||
all_materials_map: Dict[int, Material] = {}
|
all_materials_map: Dict[int, Material] = {}
|
||||||
|
all_materials_name: Dict[str, Material] = {}
|
||||||
|
|
||||||
|
|
||||||
async def fetch_materials(data: Children):
|
async def fetch_materials(data: Children):
|
||||||
@ -46,6 +47,7 @@ async def fetch_materials(data: Children):
|
|||||||
)
|
)
|
||||||
all_materials.append(material)
|
all_materials.append(material)
|
||||||
all_materials_map[material.id] = material
|
all_materials_map[material.id] = material
|
||||||
|
all_materials_name[material.name] = material
|
||||||
|
|
||||||
|
|
||||||
async def fetch_info(material: Material):
|
async def fetch_info(material: Material):
|
||||||
@ -78,3 +80,13 @@ async def dump_materials(path: Path):
|
|||||||
data = [material.dict() for material in all_materials]
|
data = [material.dict() for material in all_materials]
|
||||||
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
async with aiofiles.open(path, "w", encoding="utf-8") as f:
|
||||||
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
|
await f.write(ujson.dumps(data, indent=4, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
|
async def read_materials(path: Path):
|
||||||
|
async with aiofiles.open(path, "r", encoding="utf-8") as f:
|
||||||
|
data = ujson.loads(await f.read())
|
||||||
|
for material in data:
|
||||||
|
m = Material(**material)
|
||||||
|
all_materials.append(m)
|
||||||
|
all_materials_map[m.id] = m
|
||||||
|
all_materials_name[m.name] = m
|
||||||
|
17
main.py
17
main.py
@ -1,17 +1,24 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from func.fetch_all import get_list
|
from func.fetch_all import get_list
|
||||||
from func.fetch_materials import fetch_materials, fetch_materials_infos, dump_materials
|
from func.fetch_avatars import fetch_avatars, fetch_avatars_infos, dump_avatars, read_avatars
|
||||||
|
from func.fetch_materials import fetch_materials, fetch_materials_infos, dump_materials, read_materials
|
||||||
|
|
||||||
data_path = Path("data")
|
data_path = Path("data")
|
||||||
data_path.mkdir(exist_ok=True)
|
data_path.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main(override: bool = True):
|
||||||
main_data = await get_list()
|
main_data = await get_list()
|
||||||
await fetch_materials(main_data[4])
|
if override:
|
||||||
await fetch_materials_infos()
|
await fetch_materials(main_data[4])
|
||||||
await dump_materials(data_path / "materials.json")
|
await fetch_materials_infos()
|
||||||
|
await dump_materials(data_path / "materials.json")
|
||||||
|
else:
|
||||||
|
await read_materials(data_path / "materials.json")
|
||||||
|
await fetch_avatars(main_data[0])
|
||||||
|
await fetch_avatars_infos()
|
||||||
|
await dump_avatars(data_path / "avatars.json")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -1,45 +1,30 @@
|
|||||||
from typing import List, TYPE_CHECKING
|
from typing import List
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from models.enums import Quality, Destiny, Element
|
from models.enums import Quality, Destiny, Element
|
||||||
|
from models.material import Material
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from models.material import Material
|
|
||||||
|
|
||||||
|
|
||||||
class AvatarInfo(BaseModel):
|
class AvatarInfo(BaseModel):
|
||||||
occupation: str
|
occupation: str = ""
|
||||||
"""所属"""
|
"""所属"""
|
||||||
faction: str
|
faction: str = ""
|
||||||
"""派系"""
|
"""派系"""
|
||||||
|
|
||||||
|
|
||||||
class AvatarItem(BaseModel):
|
class AvatarItem(BaseModel):
|
||||||
item: "Material"
|
item: Material
|
||||||
"""物品"""
|
"""物品"""
|
||||||
count: int
|
count: int
|
||||||
"""数量"""
|
"""数量"""
|
||||||
|
|
||||||
|
|
||||||
class AvatarPromote(BaseModel):
|
class AvatarPromote(BaseModel):
|
||||||
required_level: int = 0
|
required_level: int
|
||||||
"""突破所需等级"""
|
"""突破所需等级"""
|
||||||
promote_level: int = 0
|
promote_level: int = 0
|
||||||
"""突破等级"""
|
"""突破等级"""
|
||||||
max_level: int
|
max_level: int
|
||||||
"""解锁的等级上限"""
|
"""解锁的等级上限"""
|
||||||
before_hp: int
|
|
||||||
"""突破前生命值"""
|
|
||||||
after_hp: int
|
|
||||||
"""突破后生命值"""
|
|
||||||
before_attack: int
|
|
||||||
"""突破前攻击力"""
|
|
||||||
after_attack: int
|
|
||||||
"""突破后攻击力"""
|
|
||||||
before_defense: int
|
|
||||||
"""突破前防御力"""
|
|
||||||
after_defense: int
|
|
||||||
"""突破后防御力"""
|
|
||||||
|
|
||||||
coin: int = 0
|
coin: int = 0
|
||||||
"""信用点"""
|
"""信用点"""
|
||||||
|
Loading…
Reference in New Issue
Block a user