Python获取暗黑破坏神3战网前1000命位玩家的英雄技能统计
作者:JinnLynn 发布时间:2023-09-25 13:01:40
标签:Python,统计
说实在的个人对游戏并没有多大的兴趣,但唯独对暴雪的Diablo系列很有感情,去年年初开始玩Diablo3,断断续续,感觉最麻烦的是选择技能,每次版本更新可能都有更优的build,这对于我这样的业余玩家来说可不是件好事,好在宏伟秘境后有了天梯,借鉴排名在前的高级玩家们build总没错,于是花了点时间写了这个脚本。
脚本只是统计了主动技能、被动技能和传奇宝石的使用情况,理论上统计其它如装备等信息也是一样简单可行的,但Diablo装备的生成机制使得统计这个没有多大意义,相同的装备属性可能各有优劣,难以比较,而且某些装备坑爹的掉率也不是你想要就能有的。
题外话,不得不说Python太适合写这类功能相对简单的脚本了,一个字:快。
# -*- coding: utf-8 -*-
"""
Diablo3 排名前1000玩家英雄使用技能统计
python diablo.py help
python diablo.py [barbarian|crusader|demon-hunter|monk'|witch-doctor|wizard]
默认使用的是亚服的数据,如果需要 * 或欧服,更改`_rank_page`和`_api`变量地址即可
Copyright (c) 2015 JinnLynn <eatfishlin@gmail.com>
Released under the terms of the MIT license.
"""
from __future__ import unicode_literals, print_function, absolute_import
import os
import sys
import urllib2
import json
import re
__version__ = '1.0.0'
__author__ = 'JinnLynn <eatfishlin@gmail.com>'
__license__ = 'The MIT License'
__copyright__ = 'Copyright 2015 JinnLynn'
# 排名页面
_rank_page = 'http://tw.battle.net/d3/zh/rankings/'
# api
_api = 'http://tw.battle.net/api/d3/'
_api_profile = os.path.join(_api, 'profile')
_api_data = os.path.join(_api, 'data')
_hero_classes = {
'barbarian': '野蠻人', 'crusader': '聖教軍', 'demon-hunter': '狩魔獵人',
'monk': '武僧', 'witch-doctor': '巫醫', 'wizard': '秘術師'}
_retry = 5
_hero_class = ''
_active_skills = {}
_passive_skills = {}
_unique_gems = {}
def _clear_output(msg=''):
sys.stdout.write('\r{:30}'.format(' '))
sys.stdout.write('\r{}'.format(msg))
sys.stdout.flush()
def _process(stated, total):
msg = '英雄数据分析中... {}/{}'.format(stated, total)
_clear_output(msg)
def _get(url, is_json=True):
# print('GET: ', url)
retry = 5 if _retry < 1 else _retry
while retry > 0:
try:
req = urllib2.urlopen(url.encode('utf8'), timeout=10)
return json.load(req) if is_json else req.read()
except KeyboardInterrupt, e:
raise e
except Exception, e:
retry -= 1
# print('retry', retry, e)
# raise e
def _api_url(*args, **kwargs):
slash = kwargs.get('slash', False)
args = [unicode(arg) for arg in args]
url = os.path.join(*args).rstrip('/')
return url + '/' if slash else url
def get_era():
req = urllib2.urlopen(_rank_page)
return req.geturl().split('/')[-2]
def get_rank_page_url(era):
url_part = 'rift-'
if _hero_class == 'demon-hunter':
url_part += 'dh'
elif _hero_class == 'witch-doctor':
url_part += 'wd'
else:
url_part += _hero_class
return os.path.join(_rank_page, 'era', era, url_part)
def fetch_rank_list():
tags = []
try:
_clear_output('获取当前游戏 * ...')
era = get_era()
_clear_output('获取当前排名前1000的玩家...')
url = get_rank_page_url(era)
html = _get(url, is_json=False)
# re parse
lst = re.findall(
r"a href=\"(.*)\" title=.*class=\"icon-profile link-first\">",
html.decode('utf8'),
re.UNICODE)
# BeautifulSoup parse
# import bs4
# soup = bs4.BeautifulSoup(html)
# lst = soup.select('#ladders-table tbody tr .battletag a')['href']
for item in lst:
try:
tags.append(item.split('/')[-2])
except:
pass
except Exception, e:
print('fetch rank list fail. {}'.format(_rank_page))
raise e
return tags
def get_hero(player_tag):
url = _api_url(_api_profile, player_tag, slash=True)
data = _get(url)
hero_selected = None
for hero in data.get('heroes', []):
if hero['class'] != _hero_class:
continue
last_updated = hero_selected['last-updated']
# 最近使用的英雄
if hero_selected is None or last_updated < hero['last-updated']:
hero_selected = hero
if not hero_selected:
raise Exception('{} hero missing.'.format(player_tag))
url = _api_url(_api_profile, player_tag, 'hero', hero_selected['id'])
return _get(url)
# 主动技能符文
def stat_active_skill_rune(skill_slug, rune):
global _active_skills
if not rune:
return
slug = rune.get('slug')
if slug in _active_skills[skill_slug]['rune']:
_active_skills[skill_slug]['rune'][slug]['count'] += 1
else:
_active_skills[skill_slug]['rune'][slug] = {
'count': 1,
'name': rune.get('name')
}
# 主动技能
def stat_active_skill(active):
global _active_skills
slug = active.get('skill', {}).get('slug')
# d3 API 返回的数据中可能存在空的数据
if not slug:
return
if slug in _active_skills:
_active_skills[slug]['count'] += 1
else:
_active_skills[slug] = {
'count': 1,
'name': active.get('skill').get('name'),
'rune': {}
}
stat_active_skill_rune(slug, active.get('rune'))
# 被动技能
def stat_passive_skill(passive):
global _passive_skills
slug = passive.get('skill', {}).get('slug')
# d3 API 返回的数据中可能存在空的数据
if not slug:
return
if slug in _passive_skills:
_passive_skills[slug]['count'] += 1
else:
_passive_skills[slug] = {
'count': 1,
'name': passive.get('skill').get('name')
}
def stat_unique_gem(items):
global _unique_gems
def get_gem(tooltip):
if not tooltip:
return None, None
url = _api_url(_api_data, tooltip)
data = _get(url)
gems = data.get('gems')
if not gems:
return None, None
gem = gems[0].get('item', {})
return gem.get('id'), gem.get('name')
if not items:
return
lst = [items.get(s, {}) for s in ['leftFinger', 'rightFinger', 'neck']]
for tooltip in [d.get('tooltipParams', None) for d in lst]:
id_, name = get_gem(tooltip)
if not id_:
continue
if id_ in _unique_gems:
_unique_gems[id_]['count'] += 1
else:
_unique_gems[id_] = {
'count': 1,
'name': name
}
def stat(hero):
global _active_skills, _passive_skills
map(stat_active_skill, hero.get('skills', {}).get('active', []))
map(stat_passive_skill, hero.get('skills', {}).get('passive', []))
items = hero.get('items', {})
stat_unique_gem(items)
def output(hero_stated, hero_stat_failed):
def sort(data, count=10):
d = sorted(data.items(), key=lambda d: d[1]['count'], reverse=True)
return d if count <= 0 else d[0:count]
_clear_output()
# print('======')
# print(hero_stated, hero_stat_failed)
# print('======')
# pprint(_active_skills)
# print('======')
# pprint(_passive_skills)
# print('======')
# pprint(_unique_gems)
# pprint(_active_skills.items())
# print('======')
print('\n=== RESULT ===\n')
print('统计英雄数\n')
print(' 成功: {} 失败: {}\n'.format(hero_stated, hero_stat_failed))
print('主动技能使用排名: ')
for _, d in sort(_active_skills):
runes = []
for _, r in sort(d.get('rune', {})):
runes.append('{name}[{count}]'.format(**r))
d.update({'rune_rank': ', '.join(runes)})
print(' {name}[{count}]: {rune_rank}'.format(**d))
print()
print('被动技能使用排名: ')
for _, d in sort(_passive_skills):
print(' {name}[{count}]'.format(**d))
print()
print('传奇宝石使用排名: ')
for _, d in sort(_unique_gems):
print(' {name}[{count}]'.format(**d))
print()
def prepare():
global _hero_class
def print_hc():
print('仅支持以下英雄类型, 默认 demon-hunter:\n')
for c, n in _hero_classes.items():
print(c, ':', n)
if len(sys.argv) == 1:
_hero_class = 'demon-hunter'
elif len(sys.argv) > 2:
sys.exit('参数错误')
else:
arg = sys.argv[1]
if arg == 'help':
print_hc()
print('\nTips: 运行中可随时Ctrl+C终止以获得已统计的数据结果')
sys.exit()
elif arg not in _hero_classes:
print_hc()
sys.exit()
else:
_hero_class = arg
def main():
prepare()
print('待分析的英雄类型:', _hero_classes[_hero_class])
hero_stated = 0
hero_stat_failed = 0
try:
tags = fetch_rank_list()
if not tags:
raise Exception('parse battle.net rank page fail.')
except Exception, e:
print('error,', e)
sys.exit()
total = len(tags)
for tag in tags:
try:
hero = get_hero(tag)
if not hero:
raise Exception('no hero data')
stat(hero)
hero_stated += 1
_process(hero_stated, total)
except KeyboardInterrupt:
break
except Exception, e:
# print('Fail: ', tag, e, hero)
hero_stat_failed += 1
output(hero_stated, hero_stat_failed)
if __name__ == '__main__':
main()
0
投稿
猜你喜欢
- 在我们使用一些数据的过程中,我们想要打乱数组内数据的顺序但不改变数据本身,可以通过改变索引值来实现,也就是将索引值重新随机排列,然后生成新的
- 我有一个朋友,一直纠结一个问题:arguments接受的实参是一个列表,得到的是一个像数组一样的东西,于是他想实现无限参数求和,在遍历数组求
- 上一篇说了vue单页面解决解决SEO的问题只是用php预处理了meta标签但是依然没有内容填充,所以对于内容抓取依然有些乏力,只是解决了从无
- @ResponseBody 和 @RequestBody 注解的区别1 前言在详述 @ResponseBody 和 @RequestBody
- 环境:Python+keras,后端为Tensorflow训练集:MNIST对于如何训练一个识别手写数字的神经网络,网上资源十分丰富,并且能
- 简介CountMinSketch是一种计数器,用来统计一个元素的计数,它能够以一个非常小的空间统计大量元素的计数,同时保证高的性能及准确性。
- 1、通过复制数据构造张量1.1 torch.tensor()torch.tensor([[0.1, 1.2], [2.2, 3.1], [4
- 概述今天我们要来做一个进阶的花分类问题. 不同于之前做过的鸢尾花, 这次我们会分析 102 中不同的花. 是不是很上头呀.预处理导包常规操作
- 1.网络爬虫的基本概念网络爬虫(又称网络蜘蛛,机器人),就是模拟客户端发送网络请求,接收请求响应,一种按照一定的规则,自动地抓取互联网信息的
- 目录1 matplot入门指南2 安装与导入3 图的构成3 总结1 matplot入门指南matplotlib是Python科学计算中使用最
- 在使用SQL Server 的过程,中由于经常需要从多个不同地点将数据集中起来或向多个地点复制数据,所以数据的导出,导入是极为常见的操作.我
- 一、作用主要用于保留组件状态或避免重新渲染。二、用法<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,
- python3.6.2环境安装配置图文教程,具体如下一、需要下载的软件》python3.6.2.exe (也可以选择更新的版本) ----
- 在这个由两部分组成的系列文章的第二部分中,我们将继续探索如何将函数式编程方法中的好想法引入到 Python中,以实现两全其美。在上一篇文章中
- 介绍获取协程返回值的四种方式:1、通过ensure_future获取,本质是future对象中的result方2、使用loop自带的crea
- 前一阵子经理问我能不能把用户最后一次登录我们的业务数据库的时间记录下来,因为之前有人修改过数据库sa用户的登录密码,所以我们要记录一下。 我
- 在备份数据库的时候,数据表中可能存在这样的值array('a'='b','c'='d
- 求解列表中元素的排列和组合问题这个问题之前就遇到过几次没有太留意,最近在做题的时候遇上挺多的排列组合问题的,想来有必要温习一下了
- 一般删除文件时使用os库,然后利用os.remove(path)即可完成删除,如果删除空文件夹则可使用os.removedirs(path)
- key_len的含义在MySQL中,可以通过explain查看SQL语句所走的路径,如下所示:mysql> create table