[Python] 纯文本查看 复制代码 import os
import requests
from lxml import etree
from concurrent.futures import ThreadPoolExecutor, as_completed
from time import sleep
from tqdm import tqdm
import logging
import argparse
# 设置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
def get_hero_list():
hero_list_url = 'https://pvp.qq.com/web201605/js/herolist.json'
try:
response = requests.get(hero_list_url, headers=headers, timeout=10)
response.raise_for_status()
return response.json()
except requests.RequestException as e:
logger.error(f"获取英雄列表失败: {e}")
return []
def download_skin_image(ename, cname, skin_name, index, max_retries=3):
skin_url = f'https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/{ename}/{ename}-bigskin-{index + 1}.jpg'
for attempt in range(max_retries):
try:
response = requests.get(skin_url, headers=headers, timeout=10)
response.raise_for_status()
file_path = os.path.join(cname, f'{skin_name}.jpg')
with open(file_path, 'wb') as f:
f.write(response.content)
logger.info(f'已下载:{skin_name}')
return True
except requests.RequestException as e:
logger.warning(f'下载 {skin_name} 失败 (尝试 {attempt + 1}/{max_retries}): {e}')
sleep(1)
logger.error(f'下载 {skin_name} 失败,已达到最大重试次数')
return False
def get_skin_names(ename, id_name):
hero_info_url = f'https://pvp.qq.com/web201605/herodetail/{id_name}.shtml'
try:
hero_info_resp = requests.get(hero_info_url, headers=headers, timeout=10)
hero_info_resp.raise_for_status()
hero_info_resp.encoding = 'gbk'
e = etree.HTML(hero_info_resp.text)
names = e.xpath('//ul[@class="pic-pf-list pic-pf-list3"]/@data-imgname')
if names:
return [name.split('&')[0] for name in names[0].split('|')]
else:
logger.warning(f'未找到英雄 {ename} 的皮肤信息')
return []
except requests.RequestException as e:
logger.error(f'获取英雄 {ename} 皮肤信息失败: {e}')
return []
def download_hero_skins(hero):
cname = hero.get('cname')
ename = str(hero.get('ename'))
id_name = hero.get('id_name')
if not os.path.exists(cname):
os.makedirs(cname)
skin_names = get_skin_names(ename, id_name)
if not skin_names:
return
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [
executor.submit(download_skin_image, ename, cname, skin_name, i)
for i, skin_name in enumerate(skin_names)
]
for future in as_completed(futures):
future.result()
def main(hero_name=None):
hero_list = get_hero_list()
if not hero_list:
logger.error("无法获取英雄列表,程序退出")
return
if hero_name:
hero_list = [hero for hero in hero_list if hero['cname'] == hero_name]
if not hero_list:
logger.error(f"未找到英雄: {hero_name}")
return
with tqdm(total=len(hero_list), desc="下载进度", unit="英雄") as pbar:
for hero in hero_list:
download_hero_skins(hero)
pbar.update(1)
sleep(1) # 防止过多请求导致服务器封锁
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="下载王者荣耀英雄皮肤")
parser.add_argument("--hero", help="指定要下载的英雄名称")
args = parser.parse_args()
main(args.hero)
错误处理和日志:
使用 logging 模块替代 print,提供更详细的日志信息。
在关键操作中增加了异常处理,并添加了重试机制。
用户体验:
使用 tqdm 库添加了进度条,让用户能够直观地看到下载进度。
添加了命令行参数解析,允许用户指定要下载的特定英雄。
性能和稳定性:
在 download_skin_image 函数中添加了重试机制,提高下载成功率。
使用 raise_for_status() 来检查 HTTP 响应状态。
代码结构和可读性:
重构了部分函数,提高了代码的可读性和可维护性。
使用 os.path.join() 来创建文件路径,提高了跨平台兼容性。
灵活性:
允许用户通过命令行参数指定要下载的英雄,增加了脚本的灵活性。
使用方法:
下载所有英雄的皮肤:python script.py
下载特定英雄的皮肤:python script.py --hero 李白 |