用 Python 分析了 20 萬場吃雞數(shù)據(jù)缀皱,看看玩家群體是怎么樣的

首先,神槍鎮(zhèn)樓

背景

最近老板愛上了吃雞(手游:全軍出擊)动猬,經(jīng)常拉著我們開黑啤斗,只能放棄午休的時間,陪老板在沙漠里奔波枣察。 上周在在微信游戲頻道看戰(zhàn)績的時候突發(fā)奇想争占,是不是可以通過這個方式抓取到很多戰(zhàn)斗數(shù)據(jù)燃逻,然后分析看看有什么規(guī)律。


秀一波戰(zhàn)績臂痕,開黑情況下我們團(tuán)隊吃雞率非常高伯襟,近100場吃雞次數(shù)51次

簡單評估了一下,覺得可行握童,咱就開始姆怪。

Step 1 分析數(shù)據(jù)接口

第一步當(dāng)然是把這些戰(zhàn)績數(shù)據(jù)采集下來,首先我們需要了解頁面背后的故事澡绩。去看看頁面是如何獲取戰(zhàn)斗數(shù)據(jù)的稽揭。

使用Charles抓包

抓包實(shí)現(xiàn)

在Mac下推薦使用工具Charles來從協(xié)議層抓取手機(jī)上的流量,原理就是在Mac上開啟一個代理服務(wù)器肥卡,然后將手機(jī)的網(wǎng)絡(luò)代理設(shè)置為Mac溪掀,這樣手機(jī)上的所有流量都會經(jīng)過我們的代理服務(wù)器了。 大致流程如下:

https加密流量的處理

在實(shí)際操作的時候發(fā)現(xiàn)微信所有的流量都走了HTTPS步鉴,導(dǎo)致我們的抓到的都是加密數(shù)據(jù)揪胃,對我們沒有任何參考意義。 經(jīng)過研究氛琢,可以通過在手機(jī)和電腦都安裝Charles根證書的方式來實(shí)現(xiàn)對Https流量的分析喊递,具體操作可以參考:

charles mac下https抓包和iphone https抓包

解決Charles無法正常抓包iOS 11中的Https請求

安裝證書后,我們的流量大致是這樣子的經(jīng)過上述的配置阳似,我們已經(jīng)可以讀取到https的請求和響應(yīng)數(shù)據(jù)了骚勘,如下圖所示。

windows下用findler可以實(shí)現(xiàn)相同的功能

其實(shí)這就是一個非常典型的中間人場景

數(shù)據(jù)接口

接下來就根據(jù)這些數(shù)據(jù)來找出我們需要的接口了撮奏,經(jīng)過分析俏讹,主要涉及三個接口

獲取用戶信息接口

獲取用戶戰(zhàn)績列表接口

獲取用戶指定戰(zhàn)績詳細(xì)信息接口

下面我們一個一個看

1. 獲取用戶信息接口

request

API/cgi-bin/gamewap/getpubgmbattlelist

方法GET

參數(shù)openid、pass_ticket挽荡、plat_id藐石、after_time、limit

cookiekey pass_ticket定拟、uin于微、pgv_pvid、sd_cookie_crttime青自、sd_userid

response

{

????"user_info": {

????????"openid": "oODfo0pjBQkcNuR4XLTQ321xFVws",

????????"head_img_url": "http://wx.qlogo.cn/mmhead/Q3auHgzwzM5hSWxxxxxUQPwW9ibxxxx9DlxLTsKWk97oWpDI0rg/96",

????????"nick_name": "望",

????????"role_name": "xxxx",

????????"zone_area_id": 0,

????????"plat_id": 1

????},

????"battle_info": {

????????"total_1": 75,

????????"total_10": 336,

????????"total_game": 745,

????????"total_kill": 1669

????},

????"battle_list": [{

????????"map_id": 1,

????????"room_id": "6575389198189071197",

????????"team_id": 57,

????????"dt_event_time": 1530953799,

????????"rank_in_ds": 3,

????????"times_kill": 1,

????????"label": "前五",

????????"team_type": 1,

????????"award_gold": 677,

????????"mode": 0

????}],

????"appitem": {

????????"AppID": "wx13051697527efc45",

????????"IconURL": "https://mmocgame.qpic.cn/wechatgame/mEMdfrX5RU0dZFfNEdCsMJpfsof1HE0TP3cfZiboX0ZPxqh5aZnHjxPFXUGgsXmibe/0",

????????"Name": "絕地求生 全軍出擊",

????????"BriefName": "絕地求生 全軍出擊",

????????"Desc": "官方正版絕地求生手游",

????????"Brief": "槍戰(zhàn) | 808.2M",

????????"WebURL": "https://game.weixin.qq.com/cgi-bin/h5/static/detail_v2/index.html?wechat_pkgid=detail_v2&appid=wx13051697527efc45&show_bubble=0",

????????"DownloadInfo": {

????????????"DownloadURL": "https://itunes.apple.com/cn/app/id1304987143",

????????????"DownloadFlag": 5

????????},

????????"Status": 0,

????????"AppInfoFlag": 45,

????????"Label": [],

????????"AppStorePopUpDialogConfig": {

????????????"Duration": 1500,

????????????"Interval": 172800,

????????????"ServerTimestamp": 1531066098

????????},

????????"HasEnabledChatGroup": false,

????????"AppType": 0,

????????"game_tag_list": ["絕地求生", "正版還原", "好友開黑", "百人對戰(zhàn)", "超大地圖"],

????????"recommend_reason": "正版絕地求生株依,荒野射擊",

????????"size_desc": "808.2M"

????},

????"is_guest": true,

????"is_blocked": false,

????"errcode": 0,

????"errmsg": "ok"

}

2. 獲取用戶戰(zhàn)績列表接口

分析

openid是用戶的惟一標(biāo)識。

2. 獲取用戶戰(zhàn)績列表接口

request

API/cgi-bin/gamewap/getpubgmbattlelist

方法GET

參數(shù)openid延窜、pass_ticket恋腕、plat_id、after_time逆瑞、limit

cookiekey pass_ticket荠藤、uin伙单、pgv_pvid、sd_cookie_crttime哈肖、sd_userid

response

{

"errcode": 0,

"errmsg": "ok",

"next_after_time": 1528120556,

"battle_list": [{

????"map_id": 1,

????"room_id": "6575389198111172597",

????"team_id": 57,

????"dt_event_time": 1530953799,

????"rank_in_ds": 3,

????"times_kill": 1,

????"label": "前五",

????"team_type": 1,

????"award_gold": 677,

????"mode": 0

}, {

????"map_id": 1,

????"room_id": "6575336498940384115",

????"team_id": 11,

????"dt_event_time": 1530941404,

????"rank_in_ds": 5,

????"times_kill": 2,

????"label": "前五",

????"team_type": 1,

????"award_gold": 632,

????"mode": 0

}],

"has_next": true

}

分析

這個接口用after_time來進(jìn)行分頁吻育,遍歷獲取時可以根據(jù)接口響應(yīng)的has_next和next_after_time來判斷是否還有下一頁的數(shù)據(jù)。

列表里面的room_id是每一場battle的惟一標(biāo)識淤井。

3. 獲取用戶戰(zhàn)績詳情接口

request

API/cgi-bin/gamewap/getpubgmbattledetail

方法GET

參數(shù)openid布疼、pass_ticket、room_id

cookiekey pass_ticket币狠、uin游两、pgv_pvid、sd_cookie_crttime漩绵、sd_userid

request


{

"errcode": 0,

"errmsg": "ok",

"base_info": {

????"nick_name": "柚茶",

????"head_img_url": "http://wx.qlogo.cn/mmhead/xxxx/96",

????"dt_event_time": 1528648165,

????"team_type": 4,

????"rank": 1,

????"player_count": 100,

????"role_sex": 1,

????"label": "大吉大利",

????"openid": "oODfo0s1w5lWjmxxxxxgQkcCljXQ"

},

"battle_info": {

????"award_gold": 622,

????"times_kill": 6,

????"times_head_shot": 0,

????"damage": 537,

????"times_assist": 3,

????"survival_duration": 1629,

????"times_save": 0,

????"times_reborn": 0,

????"vehicle_kill": 1,

????"forward_distance": 10140,

????"driving_distance": 5934,

????"dead_poison_circle_no": 6,

????"top_kill_distance": 223,

????"top_kill_distance_weapon_use": 2924130819,

????"be_kill_user": {

????????"nick_name": "小旭",

????????"head_img_url": "http://wx.qlogo.cn/mmhead/ibLButGMnqJNFsUtStNEV8tzlH1QpwPiaF9kxxxxx66G3ibjic6Ng2Rcg/96",

????????"weapon_use": 20101000001,

????????"openid": "oODfo0qrPLExxxxc0QKjFPnPxyI"

????},

????"label": "大吉大利"

},

"team_info": {

????"user_list": [{

????????"nick_name": "ooo",

????????"times_kill": 6,

????????"assist_count": 3,

????????"survival_duration": 1638,

????????"award_gold": 632,

????????"head_img_url": "http://wx.qlogo.cn/mmhead/Q3auHgzwzM4k4RXdyxavNxxxxUjcX6Tl47MNNV1dZDliazRKRg",

????????"openid": "oODfo0xxxxf1bRAXE-q-lEezK0k"

????}, {

????????"nick_name": "我吃炒肉",

????????"times_kill": 2,

????????"assist_count": 2,

????????"survival_duration": 1502,

????????"award_gold": 583,

????????"head_img_url": "http://wx.qlogo.cn/mmhead/sTJptKvBQLKd5SAAjOF0VrwiapUxxxxFffxoDUcrVjYbDf9pNENQ",

????????"openid": "oODfo0gIyDxxxxZpUrSrpapZSDT0"

????}]

},

"is_guest": true,

"is_blocked": false

}

分析

這個接口響應(yīng)了戰(zhàn)斗的詳細(xì)信息贱案,包括殺人數(shù)、爆頭數(shù)渐行、救人數(shù)轰坊、跑動距離等等,足夠我們分析了祟印。

這個接口還響應(yīng)了是被誰殺死的以及組團(tuán)成員的openid,利用這個特性我們這可無限深度的發(fā)散爬取更多用戶的數(shù)據(jù)粟害。

至于cookie中的息pass_ticket等信息肯定是用于權(quán)限認(rèn)證的蕴忆,在上述的幾次請求中這些信息都沒有變化,所以我們不需要深研其是怎么算出來的悲幅,只需要抓包提取到默認(rèn)信息后填到代碼里面就可以用了套鹅。

Step 2 爬取數(shù)據(jù)

接口已經(jīng)確定下來了,接下來就是去抓取足夠量的數(shù)據(jù)了汰具。

使用requests請求接口獲取數(shù)據(jù)

url = 'https://game.weixin.qq.com/cgi-bin/gamewap/getpubgmdatacenterindex?openid=%s&plat_id=0&uin=&key=&pass_ticket=%s' % (openid, settings.pass_ticket)

????r = requests.get(url=url, cookies=settings.def_cookies, headers=settings.def_headers, timeout=(5.0, 5.0))

????tmp = r.json()

????wfile = os.path.join(settings.Res_UserInfo_Dir, '%s.txt' % (rediskeys.user(openid)))


????with codecs.open(wfile, 'w', 'utf-8') as wf:

????????wf.write(simplejson.dumps(tmp, indent=2, sort_keys=True, ensure_ascii=False))

參照這種方式我們可以很快把另外兩個接口寫好卓鹿。

使用redis來標(biāo)記已經(jīng)爬取過的信息

在上述接口中我們可能從用戶A的入口進(jìn)去找到用戶B的openid,然后從用戶B的入口進(jìn)去又找到用戶A的openid留荔,為了避免重復(fù)采集吟孙,所以我們需要記錄下哪些信息是我們采集過的。 核心代碼片斷

# rediskeys.user_battle_list 根據(jù)openid獲取存在redis中的key值

def user_battle_list(openid):

????return 'ubl_%s' % (openid)

# 在提取battle list之前聚蝶,首先判斷這用用戶的數(shù)據(jù)是否已經(jīng)提取過了

if settings.DataRedis.get(rediskeys.user_battle_list(openid)):

????????return True

# 在提取battle list之后杰妓,需要在redis中記錄用戶信息

settings.DataRedis.set(rediskeys.user_battle_list(openid), 1)

使用celery來管理隊列

celery是一個非常好用的分布式隊列管理工具,我這次只打算在我自己的電腦上運(yùn)行碘勉,所以并沒有用到分布式的功能巷挥。 我們創(chuàng)建三個task和三個queue

task_queues = (

????Queue('queue_get_battle_info', exchange=Exchange('priority', type='direct'), routing_key='gbi'),

????Queue('queue_get_battle_list', exchange=Exchange('priority', type='direct'), routing_key='gbl'),

????Queue('queue_get_user_info', exchange=Exchange('priority', type='direct'), routing_key='gui'),

)


task_routes = ([

????('get_battle_info', {'queue': 'queue_get_battle_info'}),

????('get_battle_list', {'queue': 'queue_get_battle_list'}),

????('get_user_info', {'queue': 'queue_get_user_info'}),

],)

然后在task中控制API請求和Redis數(shù)據(jù)實(shí)現(xiàn)完整的任務(wù)邏輯,如:


@app.task(name='get_battle_list')

def get_battle_list(openid, plat_id=None, after_time=0, update_time=None):

????# 判斷是否已經(jīng)取過用戶戰(zhàn)績列表信息

????if settings.DataRedis.get(rediskeys.user_battle_list(openid)):

????????return True


????if not plat_id:

????????try:

????????????# 提取用戶信息

????????????us = handles.get_user_info_handles(openid)

????????????plat_id=us['plat_id']

????????except Exception as e:

????????????print 'can not get user plat_id', openid, traceback.format_exc()

????????????return False

????# 提取戰(zhàn)績列表

????battle_list = handles.get_battle_list_handle(openid, plat_id, after_time=0, update_time=None)


????# 為每一場戰(zhàn)斗創(chuàng)建異步獲取詳情任務(wù)

????for room_id in battle_list:

????????if not settings.DataRedis.get(rediskeys.user_battle(openid, room_id)):

????????????get_battle_info.delay(openid, plat_id, room_id)


????return True

開始抓取

因?yàn)槲覀兪前l(fā)散是爬蟲验靡,所以需要給代碼一個用戶的入口倍宾,所以需要手動創(chuàng)建一個用戶的采集任務(wù)


# 啟動獲取用戶詳情worker

celery -A tasks.all worker -c 5 --queue=queue_get_user_info --loglevel=info -n get_user_info@%h


# 啟動獲取戰(zhàn)績列表worker

celery -A tasks.all worker -c 5 --queue=queue_get_battle_list --loglevel=info -n get_battle_list@%h


# 啟動獲取戰(zhàn)績詳情worker

celery -A tasks.all worker -c 30 --queue=queue_get_battle_info --loglevel=info -n get_battle_info@%h

這樣我們的爬蟲就可以愉快的跑起來了雏节。再通過celery-flower來查看執(zhí)行情況。

Python

1celery flower -A tasks.all --broker=redis://:$REDIS_PASS@$REDIS_HOST:$REDIS_PORT/10

通過flower高职,我們可以看到運(yùn)行的效率還是非常不錯的矾屯。在執(zhí)行過程中會發(fā)現(xiàn)get_battle_list跑太快,導(dǎo)致get_battle_info即使開了30個并發(fā)都還會積壓很多初厚,所以需要適時的去停一下這些worker件蚕。 在我們抓到20萬條信息之后就可以停下來了。

Step 3 數(shù)據(jù)分析

分析方案

20萬場戰(zhàn)斗的數(shù)據(jù)已經(jīng)抓取好了产禾,全部分成json文件存在我本地磁盤上排作,接下來就做一些簡單的分析。 python在數(shù)據(jù)分析領(lǐng)域也非常強(qiáng)大亚情,有很多非常優(yōu)秀的庫妄痪,如pandas和NumPy,可惜我都沒有學(xué)過楞件,而且對于一個高考數(shù)學(xué)只考了70幾分的人來說衫生,數(shù)據(jù)分析實(shí)在是難,所以就自己寫了一個非常簡單的程序來做一些淺度分析土浸。 需要進(jìn)行深度分析罪针,又不想自己爬蟲的大牛可以聯(lián)系我打包這些數(shù)據(jù)黄伊。

# coding=utf-8

import os

import json

import datetime

import math


from conf import settings



class UserTeamTypeData:

????def __init__(self, team_type, player_count):

????????self.team_type = team_type

????????self.player_count = player_count

????????self.label = {}

????????self.dead_poison_circle_no = {}

????????self.count = 0

????????self.damage = 0

????????self.survival_duration = 0??# 生存時間

????????self.driving_distance = 0

????????self.forward_distance = 0

????????self.times_assist = 0??# 助攻

????????self.times_head_shot = 0

????????self.times_kill = 0

????????self.times_reborn = 0??# 被救次數(shù)

????????self.times_save = 0??# 救人次數(shù)

????????self.top_kill_distance = []

????????self.top_kill_distance_weapon_use = {}

????????self.vehicle_kill = 0??# 車輛殺死

????????self.award_gold = 0

????????self.times_reborn_by_role_sex = {0: 0, 1: 0}??# 0 男 1 女

????????self.times_save_by_role_sex = {0: 0, 1: 0}??# 0 男 1 女


????def update_dead_poison_circle_no(self, dead_poison_circle_no):

????????if dead_poison_circle_no in self.dead_poison_circle_no:

????????????self.dead_poison_circle_no[dead_poison_circle_no] += 1

????????else:

????????????self.dead_poison_circle_no[dead_poison_circle_no] = 1


????def update_times_reborn_and_save_by_role_sex(self, role, times_reborn, times_save):

????????if role not in self.times_reborn_by_role_sex:

????????????return


????????self.times_reborn_by_role_sex[role] += times_reborn

????????self.times_save_by_role_sex[role] += times_save


????def update_top_kill_distance_weapon_use(self, weaponid):

????????if weaponid not in self.top_kill_distance_weapon_use:

????????????self.top_kill_distance_weapon_use[weaponid] = 1

????????else:

????????????self.top_kill_distance_weapon_use[weaponid] += 1



class UserBattleData:


????def __init__(self, openid):

????????self.openid = openid

????????self.team_type_res = {}

????????self.label = {}

????????self.hour_counter = {}

????????self.weekday_counter = {}

????????self.usetime = 0

????????self.day_record = set()

????????self.battle_counter = 0


????def get_avg_use_time_per_day(self):

????????# print "get_avg_use_time_per_day:", self.openid, self.usetime, len(self.day_record), self.usetime / len(self.day_record)

????????return self.usetime / len(self.day_record)


????def update_label(self, lable):

????????if lable in self.label:

????????????self.label[lable] += 1

????????else:

????????????self.label[lable] = 1


????def get_team_type_data(self, team_type, player_count):

????????player_count = int(math.ceil(float(player_count) / 10))

????????team_type_key = '%d_%d' % (team_type, player_count)


????????if team_type_key not in self.team_type_res:

????????????userteamtypedata = UserTeamTypeData(team_type, player_count)

????????????self.team_type_res[team_type_key] = userteamtypedata

????????else:

????????????userteamtypedata = self.team_type_res[team_type_key]


????????return userteamtypedata


????def update_user_time_property(self, dt_event_time):

????????dt_event_time = datetime.datetime.fromtimestamp(dt_event_time)

????????hour = dt_event_time.hour

????????if hour in self.hour_counter:

????????????self.hour_counter[hour] += 1

????????else:

????????????self.hour_counter[hour] = 1


????????weekday = dt_event_time.weekday()

????????if weekday in self.weekday_counter:

????????????self.weekday_counter[weekday] += 1

????????else:

????????????self.weekday_counter[weekday] = 1


????????self.day_record.add(dt_event_time.date())


????def update_battle_info_by_room(self, roomid):

????????# print '??load ', self.openid, roomid

????????file = os.path.join(settings.Res_UserBattleInfo_Dir, self.openid, '%s.txt' % roomid)


????????with open(file, 'r') as rf:

????????????battledata = json.load(rf)


????????self.battle_counter += 1

????????base_info = battledata['base_info']

????????self.update_user_time_property(base_info['dt_event_time'])

????????battle_info = battledata['battle_info']


????????userteamtypedata = self.get_team_type_data(base_info['team_type'], base_info['player_count'])

????????userteamtypedata.count += 1

????????userteamtypedata.award_gold += battle_info['award_gold']

????????userteamtypedata.damage += battle_info['damage']

????????userteamtypedata.update_dead_poison_circle_no(battle_info['dead_poison_circle_no'])

????????userteamtypedata.driving_distance += battle_info['driving_distance']

????????userteamtypedata.forward_distance += battle_info['forward_distance']

????????self.update_label(battle_info['label'])

????????userteamtypedata.survival_duration += battle_info['survival_duration']

????????self.usetime += battle_info['survival_duration']/60

????????userteamtypedata.times_assist += battle_info['times_assist']

????????userteamtypedata.times_head_shot += battle_info['times_head_shot']

????????userteamtypedata.times_kill += battle_info['times_kill']

????????userteamtypedata.times_reborn += battle_info['times_reborn']

????????userteamtypedata.times_save += battle_info['times_save']

????????userteamtypedata.damage += battle_info['damage']

????????userteamtypedata.top_kill_distance.append(battle_info['top_kill_distance'])

????????userteamtypedata.update_times_reborn_and_save_by_role_sex(base_info['role_sex'], battle_info['times_reborn'],

??????????????????????????????????????????????????????????????????battle_info['times_save'])


????def get_user_battleinfo_rooms(self):

????????user_dir = os.path.join(settings.Res_UserBattleInfo_Dir, self.openid)

????????r = [room for room in os.listdir(user_dir)]

????????r = [rr.replace('.txt', '') for rr in r]

????????return r


class AllUserCounter:


????def __init__(self):

????????self.hour_counter = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0, 21: 0, 22: 0, 23: 0}


????????self.weekday_counter = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0}

????????self.times_reborn_by_role_sex = {0: 0, 1: 0}??# 0 男 1 女

????????self.times_save_by_role_sex = {0: 0, 1: 0}??# 0 男 1 女

????????self.user_count = 0

????????self.battle_count = 0

????????self.every_user_use_time_per_day = []

????????self.top_kill_distance = 0


????def avg_use_time(self):

????????return sum(self.every_user_use_time_per_day) / len(self.every_user_use_time_per_day)


????def add_user_data(self, userbattledata):

????????self.every_user_use_time_per_day.append(userbattledata.get_avg_use_time_per_day())

????????self.battle_count += userbattledata.battle_counter

????????self.user_count += 1


????????for k in userbattledata.hour_counter:

????????????if k in self.hour_counter:

????????????????self.hour_counter[k] += userbattledata.hour_counter[k]

????????????else:

????????????????self.hour_counter[k] = userbattledata.hour_counter[k]


????????for weekday in userbattledata.weekday_counter:

????????????if weekday in self.weekday_counter:

????????????????self.weekday_counter[weekday] += userbattledata.weekday_counter[weekday]

????????????else:

????????????????self.weekday_counter[weekday] = userbattledata.weekday_counter[weekday]


????????for userteamtype in userbattledata.team_type_res:

????????????userteamtypedata = userbattledata.team_type_res[userteamtype]

????????????for k in userteamtypedata.times_reborn_by_role_sex:

????????????????self.times_reborn_by_role_sex[k] += userteamtypedata.times_reborn_by_role_sex[k]


????????????for k in userteamtypedata.times_save_by_role_sex:

????????????????self.times_save_by_role_sex[k] += userteamtypedata.times_save_by_role_sex[k]


????????????if userteamtypedata.top_kill_distance > self.top_kill_distance:

????????????????self.top_kill_distance = userteamtypedata.top_kill_distance


????def __str__(self):

????????res = []

????????res.append('總用戶數(shù)\t%d' % self.user_count)

????????res.append('總戰(zhàn)斗數(shù)\t%d' % self.battle_count)

????????res.append('平均日耗時\t%d' % self.avg_use_time())

????????res.append('最遠(yuǎn)擊殺\t%d' % max(self.top_kill_distance))

????????res.append('男性角色\t被救%d次\t救人%d次' % (self.times_reborn_by_role_sex[0], self.times_save_by_role_sex[0]))

????????res.append('女性角色\t被救%d次\t救人%d次' % (self.times_reborn_by_role_sex[1], self.times_save_by_role_sex[1]))


????????res.append('小時分布')

????????for hour in range(0, 24):

????????????# res.append('\t%d: %d' % (hour, self.hour_counter[hour]))

????????????res.append('\t%d: %d %.2f%%' % (hour, self.hour_counter[hour], self.hour_counter[hour]/float(self.battle_count)*100))

????????res.append('星期分布')

????????# res.append(self.weekday_counter.__str__())

????????for weekday in range(0, 7):

????????????res.append('\t%d: %d %.2f%%' % (weekday+1, self.weekday_counter[weekday], (self.weekday_counter[weekday]/float(self.battle_count)*100)))


????????return '\n'.join(res)



def get_user_battleinfo_rooms(openid):

????user_dir = os.path.join(settings.Res_UserBattleInfo_Dir, openid)


????# files = os.listdir(user_dir)

????r = [room for room in os.listdir(user_dir)]

????r = [rr.replace('.txt', '') for rr in r]

????return r



if __name__ == '__main__':

????alluserconter = AllUserCounter()


????folders = os.listdir(settings.Res_UserBattleInfo_Dir)

????i = 0

????for folder in folders:

????????i+=1

????????print i, '/' , len(folders), folder

????????userbattledata = UserBattleData(folder)

????????for room in userbattledata.get_user_battleinfo_rooms():

????????????userbattledata.update_battle_info_by_room(room)

????????alluserconter.add_user_data(userbattledata)


????print "\n" * 3

????print "---------------------------------------"


????print alluserconter

分析結(jié)果

1. 平均用戶日在線時長2小時

從分布圖上看大部分用戶都在1小時以上泪酱,最猛的幾個人超過8小時。

注:我這里統(tǒng)計的是每一局的存活時間还最,實(shí)際在線時長會比我這個更長墓阀。

2. 女性角色被救次數(shù)高于男性

終于知道為什么有那么多人妖了,原來在游戲里面可以占便宜啊拓轻。

3. 女性角色救人次數(shù)高于男性

給了大家一個帶妹上分的好理由斯撮。

4. 周五大家最忙

估計周五大家都要忙著交差和寫周報了。

5. 晚上22點(diǎn)是游戲高峰

凌晨還有那么多人玩扶叉,你們不睡覺嗎勿锅?

6. 最遠(yuǎn)擊殺距離639米

我看了一下98K、SKS 和 AWP 的有效射程辜梳,大致都在 800 米以內(nèi)粱甫,所以這個值可信度還是可以的。 反過來看抖音上的那些超遠(yuǎn)距離擊殺應(yīng)該都是擺拍的作瞄。

7. 能拿到「救死扶傷」稱號才是最高榮耀

從分布情況可以看出來茶宵,救死扶傷比十殺還要難。

能拿到救死扶傷稱號的大部分都是女性角色宗挥,再一次證明玩游戲要帶妹乌庶。 回歸到這個游戲的本質(zhì)种蝶,那就是生存游戲,沒什么比活下來更重要的了瞒大。

結(jié)尾

這次爬蟲主要是利用了微信游戲頻道可以查看陌生人數(shù)據(jù)的場景才能提取到這么多數(shù)據(jù)螃征。我們可以通過同樣的手段來分析王者榮耀和其它游戲的數(shù)據(jù),有興趣的同學(xué)可以嘗試一下透敌。 最后再說一下盯滚,UMP9 是把好槍,配 2 倍鏡非常爽酗电。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末魄藕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子撵术,更是在濱河造成了極大的恐慌背率,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫩与,死亡現(xiàn)場離奇詭異寝姿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)划滋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門饵筑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人古毛,你說我怎么就攤上這事翻翩。” “怎么了稻薇?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胶征。 經(jīng)常有香客問我塞椎,道長,這世上最難降的妖魔是什么睛低? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任案狠,我火速辦了婚禮,結(jié)果婚禮上钱雷,老公的妹妹穿的比我還像新娘骂铁。我一直安慰自己,他們只是感情好罩抗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布拉庵。 她就那樣靜靜地躺著,像睡著了一般套蒂。 火紅的嫁衣襯著肌膚如雪钞支。 梳的紋絲不亂的頭發(fā)上茫蛹,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音烁挟,去河邊找鬼婴洼。 笑死,一個胖子當(dāng)著我的面吹牛撼嗓,可吹牛的內(nèi)容都是我干的柬采。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼且警,長吁一口氣:“原來是場噩夢啊……” “哼粉捻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起振湾,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤杀迹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后押搪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體树酪,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年大州,在試婚紗的時候發(fā)現(xiàn)自己被綠了续语。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡厦画,死狀恐怖疮茄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情根暑,我是刑警寧澤力试,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站排嫌,受9級特大地震影響畸裳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜淳地,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一怖糊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颇象,春花似錦伍伤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春阅爽,著一層夾襖步出監(jiān)牢的瞬間路幸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工付翁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留简肴,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓百侧,卻偏偏與公主長得像砰识,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子佣渴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容