HonkaiStarRailWikiDataParser/func/fetch_avatars.py
2023-04-23 23:18:42 +08:00

129 lines
4.3 KiB
Python

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