PaiGram/modules/wiki/character.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

202 lines
5.9 KiB
Python
Raw Normal View History

import re
from typing import List, Optional
from bs4 import BeautifulSoup
from httpx import URL
from modules.wiki.base import HONEY_HOST, Model, WikiModel
from modules.wiki.other import Association, Element, WeaponType
class Birth(Model):
"""生日
Attributes:
day:
month:
"""
day: int
month: int
class CharacterAscension(Model):
"""角色的突破材料
Attributes:
level: 等级突破材料
skill: 技能/天赋培养材料
"""
level: List[str] = []
skill: List[str] = []
class CharacterState(Model):
"""角色属性值
Attributes:
level: 等级
HP: 生命
ATK: 攻击力
DEF: 防御力
CR: 暴击率
CD: 暴击伤害
bonus: 突破属性
"""
level: str
HP: int
ATK: float
DEF: float
CR: str
CD: str
bonus: str
class CharacterIcon(Model):
icon: str
side: str
gacha: str
2024-11-30 14:32:07 +00:00
splash: Optional[str] = None
class Character(WikiModel):
"""角色
Attributes:
title: 称号
occupation: 所属
association: 地区
weapon_type: 武器类型
element: 元素
birth: 生日
constellation: 命之座
cn_cv: 中配
jp_cv: 日配
en_cv: 英配
kr_cv: 韩配
description: 描述
"""
id: str
title: str
occupation: str
association: Association
weapon_type: WeaponType
element: Element
2024-11-30 14:32:07 +00:00
birth: Optional[Birth] = None
constellation: str
cn_cv: str
jp_cv: str
en_cv: str
kr_cv: str
description: str
ascension: CharacterAscension
stats: List[CharacterState]
@classmethod
def scrape_urls(cls) -> List[URL]:
return [HONEY_HOST.join("fam_chars/?lang=CHS")]
@classmethod
async def _parse_soup(cls, soup: BeautifulSoup) -> "Character":
"""解析角色页"""
soup = soup.select(".wp-block-post-content")[0]
tables = soup.find_all("table")
table_rows = tables[0].find_all("tr")
def get_table_text(row_num: int) -> str:
"""一个快捷函数,用于返回表格对应行的最后一个单元格中的文本"""
return table_rows[row_num].find_all("td")[-1].text.replace("\xa0", "")
id_ = re.findall(r"img/(.*?_\d+)_.*", table_rows[0].find("img").attrs["src"])[0]
name = get_table_text(0)
2024-08-27 03:58:27 +00:00
if "测试" in name:
raise NotImplementedError(f"测试服数据 {name} 暂不支持")
if name != "旅行者": # 如果角色名不是 旅行者
title = get_table_text(1)
occupation = get_table_text(2)
association = Association.convert(get_table_text(3).lower().title())
rarity = len(table_rows[4].find_all("img"))
weapon_type = WeaponType[get_table_text(5)]
element = Element[get_table_text(6)]
birth = Birth(day=int(get_table_text(7)), month=int(get_table_text(8)))
constellation = get_table_text(10)
cn_cv = get_table_text(11)
jp_cv = get_table_text(12)
en_cv = get_table_text(13)
kr_cv = get_table_text(14)
else:
name = "" if id_.endswith("5") else ""
title = get_table_text(0)
occupation = get_table_text(1)
association = Association.convert(get_table_text(2).lower().title())
rarity = len(table_rows[3].find_all("img"))
weapon_type = WeaponType[get_table_text(4)]
element = Element[get_table_text(5)]
birth = None
constellation = get_table_text(7)
cn_cv = get_table_text(8)
jp_cv = get_table_text(9)
en_cv = get_table_text(10)
kr_cv = get_table_text(11)
description = get_table_text(-3)
ascension = CharacterAscension(
level=[
target[0]
for i in table_rows[-2].find_all("a")
if (target := re.findall(r"/(.*)/", i.attrs["href"])) # 过滤掉错误的材料(honey网页的bug)
],
skill=[re.findall(r"/(.*)/", i.attrs["href"])[0] for i in table_rows[-1].find_all("a")],
)
stats = []
for row in tables[2].find_all("tr")[1:]:
cells = row.find_all("td")
stats.append(
CharacterState(
level=cells[0].text,
HP=cells[1].text,
ATK=cells[2].text,
DEF=cells[3].text,
CR=cells[4].text,
CD=cells[5].text,
bonus=cells[6].text,
)
)
return Character(
id=id_,
name=name,
title=title,
occupation=occupation,
association=association,
weapon_type=weapon_type,
element=element,
birth=birth,
constellation=constellation,
cn_cv=cn_cv,
jp_cv=jp_cv,
rarity=rarity,
en_cv=en_cv,
kr_cv=kr_cv,
description=description,
ascension=ascension,
stats=stats,
)
@classmethod
async def get_url_by_name(cls, name: str) -> Optional[URL]:
# 重写此函数的目的是处理主角名字的 ID
_map = {"": "playergirl_007", "": "playerboy_005"}
if (id_ := _map.get(name)) is not None:
return await cls.get_url_by_id(id_)
return await super(Character, cls).get_url_by_name(name)
@property
def icon(self) -> CharacterIcon:
return CharacterIcon(
icon=str(HONEY_HOST.join(f"/img/{self.id}_icon.webp")),
side=str(HONEY_HOST.join(f"/img/{self.id}_side_icon.webp")),
gacha=str(HONEY_HOST.join(f"/img/{self.id}_gacha_card.webp")),
splash=str(HONEY_HOST.join(f"/img/{self.id}_gacha_splash.webp")),
)