起因

我们都知道原神抽卡是有保底机制的,但是游戏里面只能按页查看抽卡记录,并没有各种数据统计,为了能够优化大家的游戏体验,本文就带大家用java爬虫来获取抽卡信息。

抽卡信息api解析

由于我用的是安卓(鸿蒙)手机,所有就介绍安卓获取信息的办法。

对于每一个角色,在游戏内查看记录的时候都会有一个特定的api,我们需要获得这个api。

安卓(鸿蒙)的获取方法是 祈愿->查看历史记录->断网(手动断开手机的网络连接),然后把相应的api复制下来。

就是上面这个页面,断网后点击右上方橘色框中的按钮,然后就能得到你的查询链接

这个链接看起来是个get请求,将这个链接复制到浏览器中就可以得到查询页面

大概就是上面这个样子。

现在来详细看一下连接中带有的参数。

https://webstatic.mihoyo.com/hk4e/event/e20190909gacha/index.html?
authkey_ver=1&
sign_type=2&
auth_appid=webview_gacha&
init_type=301&
gacha_id=50eef7be3b7945930041f9f20310e0bbcb8a7a&
timestamp=1648597804&
lang=zh-cn&
device_type=mobile&
ext=%7b%22loc%22%3a%7b%22x%22%3a-206.83416748046876%2c%22y%22%3a201.1728973388672%2c%22z%22%3a-993.37060546875%7d%2c%22platform%22%3a%22Android%22%7d&
game_version=CNRELAndroid2.6.0_R6626848_S6641322_D6692205&
plat_type=android&
region=cn_gf01&
authkey=wR9769ie0pACSLWz2W3ggPLgAe6jwLyaCX8%2f7qE2ywEWqPDFceqGlSewiU7u0%2fS7osSNc%2fyZKtg2LOrM%2fzAsw5W%2bOEKRrtqMC4CNmARgbGag0PC5q071zjwKbQyBx4LpkPjr7%2f%2bkaBLORncy8NcB6IMiqsXDYYot6DmhbytWKPRGH6XHxZOK6avncs1B7dxJYISjv5OuyqIzfQ8fynDtei7EclBKT8GXpdfaPd3M0Dq4QfD9qd8757g%2f4wSPqbxBHwvl5yFUv0k4VQsmladDiEbeseLPDxHzB6VggTijG3UoMGgVLqSlprUAdLEpYDIsOjqMUTa5oywDBUqXF%2fvhAt7fzyx8Cf2orSUjjpJZUEEtJSltDLCJlNUJllysfbo84lOJnNeUEJdPn91D0%2bLN1UG8J7%2fRgmF9TBtbLGHzFXkMk0TGWSkuF%2f1FNKm6VJtNX7K1V4PoPdk6u%2bLbmH3Wz4SR6ZJXCIgNwor6SKrKa0RxtY6skwPuNHlMPTO9FwSo0c1FN75X8iJaNUO5MuZrPVHbLkRoTZdpjFOiRh1R0P2UY%2b5MIR6wErfS6u7kK0Zxyz8n9IUG%2fc2IfHrys0WogXmmikxXQwB8LR9%2b6N%2bB2xvNzr6tRMVIHdamAetaHMM3cXXt2Iy7FiANW2B8gYbW2ts1zvN38Bf4zC6RNfRk2w9oSvKnEUsNA5po9eaBs1YSeBYf1FCj21jRj11K8x%2ffuwRO4zSLEvdYBHp2PJzM56Ddiyxflo5Im8p3HlR1dd8IguzhYySzw7uy%2bw2F00jx9gW%2fDFR5CGKOXil%2bUfOUFsI7V1qg7GX%2fLi3dzJBu6Nt1YXmJZf6SjKuOmUNU7vEBd14MPsc%2bUen80MXUCN6a28GovAixc1IWbcS%2fBhNsxR3ZkSIaMGRLvSssgY91Ah8QJXkUr6zbFZcTXUz4LGIQUmpolI22Nj5wPX5iiDwexLGqQgwn%2fhS5MYuWDAPxeaSOIlqQuVvmva3S8d083E%2bN6XWZLs%2bSS0SZ%2bCnYQmM59G1sNNoQJUwx8krxFoWQVPSASQy4QM6pLXUYYzP2sY%2bm9TD%2bzdWcm4Z%2fe16%2bClAFbCk5Y9rYXgxIshLDUIMjhSlQJkp7rqalYPQu8IewhX1VjDbQ6fEjD%2b8gg6QeBKGytwivPCZJoSKh%2fO7ebEawvTeSVHBvw1M3iztKHB2nk6QxQiGMfh%2b56qu3Tr8KxxqI9Dq1CePwbfIRhHHP7oujqBoJ2xuAY0VCgzRFfUViPdT8gTrHE7QEE6hNi4ZpZvqkQYTTXUW%2fgqvNFYYvmGUDs19g%2b8M0gELlcmNN%2bBUFl4TUUH0n%2fjiAy1CXyMf00wUtuNHfw8smHW9f8ELlLIgQd%2bWsZA%3d%3d&
game_biz=hk4e_cn#/log

为了能看的清晰一点,我把参数都用换行隔开了,如果你要用这个链接对服务器进行请求的话,要把空格删掉。

我们按照传统的爬虫方法区看html标签,会发现,换页标签中并没有包含超链接。

这个时候就要去看网络流信息了,我们打开开发者工具后,点击Network标签页,就能看到网络流。

打开Network标签页然后刷新页面,会得到下列信息,其中包括了页面请求的各种资源,这些都不重要,重要的是我下图选中的请求

这个请求在每次换页的时候都要发一起,现在来看一下该请求的详细参数

https://hk4e-api.mihoyo.com/event/gacha_info/api/getGachaLog?
authkey_ver=1&
sign_type=2&
auth_appid=webview_gacha&
init_type=301&
gacha_id=50eef7be3b7945930041f9f20310e0bbcb8a7a&
timestamp=1648597804&
lang=zh-cn&
device_type=mobile&
ext=%7b%22loc%22%3a%7b%22x%22%3a-206.83416748046876%2c%22y%22%3a201.1728973388672%2c%22z%22%3a-993.37060546875%7d%2c%22platform%22%3a%22Android%22%7d&
game_version=CNRELAndroid2.6.0_R6626848_S6641322_D6692205&
plat_type=android&
region=cn_gf01&
authkey=wR9769ie0pACSLWz2W3ggPLgAe6jwLyaCX8%2f7qE2ywEWqPDFceqGlSewiU7u0%2fS7osSNc%2fyZKtg2LOrM%2fzAsw5W%2bOEKRrtqMC4CNmARgbGag0PC5q071zjwKbQyBx4LpkPjr7%2f%2bkaBLORncy8NcB6IMiqsXDYYot6DmhbytWKPRGH6XHxZOK6avncs1B7dxJYISjv5OuyqIzfQ8fynDtei7EclBKT8GXpdfaPd3M0Dq4QfD9qd8757g%2f4wSPqbxBHwvl5yFUv0k4VQsmladDiEbeseLPDxHzB6VggTijG3UoMGgVLqSlprUAdLEpYDIsOjqMUTa5oywDBUqXF%2fvhAt7fzyx8Cf2orSUjjpJZUEEtJSltDLCJlNUJllysfbo84lOJnNeUEJdPn91D0%2bLN1UG8J7%2fRgmF9TBtbLGHzFXkMk0TGWSkuF%2f1FNKm6VJtNX7K1V4PoPdk6u%2bLbmH3Wz4SR6ZJXCIgNwor6SKrKa0RxtY6skwPuNHlMPTO9FwSo0c1FN75X8iJaNUO5MuZrPVHbLkRoTZdpjFOiRh1R0P2UY%2b5MIR6wErfS6u7kK0Zxyz8n9IUG%2fc2IfHrys0WogXmmikxXQwB8LR9%2b6N%2bB2xvNzr6tRMVIHdamAetaHMM3cXXt2Iy7FiANW2B8gYbW2ts1zvN38Bf4zC6RNfRk2w9oSvKnEUsNA5po9eaBs1YSeBYf1FCj21jRj11K8x%2ffuwRO4zSLEvdYBHp2PJzM56Ddiyxflo5Im8p3HlR1dd8IguzhYySzw7uy%2bw2F00jx9gW%2fDFR5CGKOXil%2bUfOUFsI7V1qg7GX%2fLi3dzJBu6Nt1YXmJZf6SjKuOmUNU7vEBd14MPsc%2bUen80MXUCN6a28GovAixc1IWbcS%2fBhNsxR3ZkSIaMGRLvSssgY91Ah8QJXkUr6zbFZcTXUz4LGIQUmpolI22Nj5wPX5iiDwexLGqQgwn%2fhS5MYuWDAPxeaSOIlqQuVvmva3S8d083E%2bN6XWZLs%2bSS0SZ%2bCnYQmM59G1sNNoQJUwx8krxFoWQVPSASQy4QM6pLXUYYzP2sY%2bm9TD%2bzdWcm4Z%2fe16%2bClAFbCk5Y9rYXgxIshLDUIMjhSlQJkp7rqalYPQu8IewhX1VjDbQ6fEjD%2b8gg6QeBKGytwivPCZJoSKh%2fO7ebEawvTeSVHBvw1M3iztKHB2nk6QxQiGMfh%2b56qu3Tr8KxxqI9Dq1CePwbfIRhHHP7oujqBoJ2xuAY0VCgzRFfUViPdT8gTrHE7QEE6hNi4ZpZvqkQYTTXUW%2fgqvNFYYvmGUDs19g%2b8M0gELlcmNN%2bBUFl4TUUH0n%2fjiAy1CXyMf00wUtuNHfw8smHW9f8ELlLIgQd%2bWsZA%3d%3d&
game_biz=hk4e_cn&
gacha_type=301&
page=1&
size=6&
end_id=0

对比我们从游戏中获得的原始地址就会发现,他们的参数几乎一致,我把不一致的参数提取取出来,他们分别是

gacha_type=301&
page=1&
size=6&
end_id=0

这个就是我们分页的请求参数啦。

其中gacha_type表示的是卡池信息,游戏中一共提供了4个卡池

四个卡池的对应的数字分别是("新手祈愿",100),("常驻祈愿", 200),("活动祈愿1&2",301 ),("武器祈愿",302)。

pagesize就是分页的两个参数,比较容易理解,就不说了。

比较重要的是end_id这个参数,这个参数的值如果为0的话表示从头开始查,如果不为0就从传入的那个id开始,并且一次最多查询20条数据。

下面来看一个具体的请求例子,为了简单期间,我们把请求数量定为2,这样看起来比较直观。

{retcode: 0,message: "OK",data: {page: "5",size: "2",total: "0",list: [{uid: "198952358",gacha_type: "400",item_id: "",count: "1",time: "2022-04-09 10:59:41",name: "翡玉法球",lang: "zh-cn",item_type: "武器",rank_type: "3",id: "1649469960001828158"},{uid: "198952358",gacha_type: "400",item_id: "",count: "1",time: "2022-04-08 21:48:31",name: "以理服人",lang: "zh-cn",item_type: "武器",rank_type: "3",id: "1649423160001716358"}],region: "cn_gf01"}
}

使用java获取数据

有了上面的基础信息,相信你已经胸有成竹了。

在解析之前,需要准备好两个jar包

我们先定义一个实体类,接收接口返回的信息

public class ItemEntity {private String uid;private String item_id;private String item_type;private String count;private String name;private String gacha_type;private String time;private String id;private String lang;private String rank_type;public ItemEntity() {}public ItemEntity(String uid, String item_id, String item_type, String count, String name, String gacha_type, String time, String id, String lang, String rank_type) {this.uid = uid;this.item_id = item_id;this.item_type = item_type;this.count = count;this.name = name;this.gacha_type = gacha_type;this.time = time;this.id = id;this.lang = lang;this.rank_type = rank_type;}public String getUid() {return uid;}public void setUid(String uid) {this.uid = uid;}public String getItem_id() {return item_id;}public void setItem_id(String item_id) {this.item_id = item_id;}public String getItem_type() {return item_type;}public void setItem_type(String item_type) {this.item_type = item_type;}public String getCount() {return count;}public void setCount(String count) {this.count = count;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getGacha_type() {return gacha_type;}public void setGacha_type(String gacha_type) {this.gacha_type = gacha_type;}public String getTime() {return time;}public void setTime(String time) {this.time = time;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getLang() {return lang;}public void setLang(String lang) {this.lang = lang;}public String getRank_type() {return rank_type;}public void setRank_type(String rank_type) {this.rank_type = rank_type;}@Overridepublic String toString() {return "ItemEntity{" +"uid='" + uid + '\'' +", item_id='" + item_id + '\'' +", item_type='" + item_type + '\'' +", count='" + count + '\'' +", name='" + name + '\'' +", gacha_type='" + gacha_type + '\'' +", time='" + time + '\'' +", id='" + id + '\'' +", lang='" + lang + '\'' +", rank_type='" + rank_type + '\'' +'}';}
}

然后使用一个小案例,解析json,并且将相应信息变成上面的实体类

public class MiHaYouSoup {public static void main(String[] args) throws Exception {// 这里填你自己的urlString url = "https://hk4e-api.mihoyo.com/event/gacha_info/api/getGachaLog?authkey_ver=1&sign_type=2&auth_appid=webview_gacha&init_type=301&gacha_id=50eef7be3b7945930041f9f20310e0bbcb8a7a&timestamp=1648597804&lang=zh-cn&device_type=mobile&ext=%7b%22loc%22%3a%7b%22x%22%3a-206.83416748046876%2c%22y%22%3a201.1728973388672%2c%22z%22%3a-993.37060546875%7d%2c%22platform%22%3a%22Android%22%7d&game_version=CNRELAndroid2.6.0_R6626848_S6641322_D6692205&plat_type=android&region=cn_gf01&authkey=wR9769ie0pACSLWz2W3ggPLgAe6jwLyaCX8%2f7qE2ywEWqPDFceqGlSewiU7u0%2fS7osSNc%2fyZKtg2LOrM%2fzAsw5W%2bOEKRrtqMC4CNmARgbGag0PC5q071zjwKbQyBx4LpkPjr7%2f%2bkaBLORncy8NcB6IMiqsXDYYot6DmhbytWKPRGH6XHxZOK6avncs1B7dxJYISjv5OuyqIzfQ8fynDtei7EclBKT8GXpdfaPd3M0Dq4QfD9qd8757g%2f4wSPqbxBHwvl5yFUv0k4VQsmladDiEbeseLPDxHzB6VggTijG3UoMGgVLqSlprUAdLEpYDIsOjqMUTa5oywDBUqXF%2fvhAt7fzyx8Cf2orSUjjpJZUEEtJSltDLCJlNUJllysfbo84lOJnNeUEJdPn91D0%2bLN1UG8J7%2fRgmF9TBtbLGHzFXkMk0TGWSkuF%2f1FNKm6VJtNX7K1V4PoPdk6u%2bLbmH3Wz4SR6ZJXCIgNwor6SKrKa0RxtY6skwPuNHlMPTO9FwSo0c1FN75X8iJaNUO5MuZrPVHbLkRoTZdpjFOiRh1R0P2UY%2b5MIR6wErfS6u7kK0Zxyz8n9IUG%2fc2IfHrys0WogXmmikxXQwB8LR9%2b6N%2bB2xvNzr6tRMVIHdamAetaHMM3cXXt2Iy7FiANW2B8gYbW2ts1zvN38Bf4zC6RNfRk2w9oSvKnEUsNA5po9eaBs1YSeBYf1FCj21jRj11K8x%2ffuwRO4zSLEvdYBHp2PJzM56Ddiyxflo5Im8p3HlR1dd8IguzhYySzw7uy%2bw2F00jx9gW%2fDFR5CGKOXil%2bUfOUFsI7V1qg7GX%2fLi3dzJBu6Nt1YXmJZf6SjKuOmUNU7vEBd14MPsc%2bUen80MXUCN6a28GovAixc1IWbcS%2fBhNsxR3ZkSIaMGRLvSssgY91Ah8QJXkUr6zbFZcTXUz4LGIQUmpolI22Nj5wPX5iiDwexLGqQgwn%2fhS5MYuWDAPxeaSOIlqQuVvmva3S8d083E%2bN6XWZLs%2bSS0SZ%2bCnYQmM59G1sNNoQJUwx8krxFoWQVPSASQy4QM6pLXUYYzP2sY%2bm9TD%2bzdWcm4Z%2fe16%2bClAFbCk5Y9rYXgxIshLDUIMjhSlQJkp7rqalYPQu8IewhX1VjDbQ6fEjD%2b8gg6QeBKGytwivPCZJoSKh%2fO7ebEawvTeSVHBvw1M3iztKHB2nk6QxQiGMfh%2b56qu3Tr8KxxqI9Dq1CePwbfIRhHHP7oujqBoJ2xuAY0VCgzRFfUViPdT8gTrHE7QEE6hNi4ZpZvqkQYTTXUW%2fgqvNFYYvmGUDs19g%2b8M0gELlcmNN%2bBUFl4TUUH0n%2fjiAy1CXyMf00wUtuNHfw8smHW9f8ELlLIgQd%2bWsZA%3d%3d&game_biz=hk4e_cn&gacha_type=301&page=5&size=2&end_id=1649513160000977558";Document document = Jsoup.connect(url).header("Accept", "*/*").header("Accept-Encoding", "gzip, deflate").header("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3").header("Content-Type", "application/json;charset=UTF-8").header("Content-Type", "application/x-javascript;charset=UTF-8").header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0").timeout(10000).ignoreContentType(true).get();System.out.println(document.body().text());String jsonstr = document.body().text();// 将json字符串转为mapMap<String, Object> map1 = (Map<String, Object>) JSON.parse(jsonstr);System.out.println(map1.get("data"));// 获取data中的list信息Map<String, Object> map2 = (Map<String, Object>) JSON.parse(map1.get("data").toString());List<ItemEntity> itemEntityList = JSON.parseArray(map2.get("list").toString(), ItemEntity.class);System.out.println(itemEntityList);for(int i=0; i<itemEntityList.size(); i++){System.out.println(itemEntityList.get(i));}}}

运行上述代码,就可以发现,我们已经能够解析数据了

接下来要做的事情,就是编写更加复杂的逻辑,具体要做的事情就是,一个主接口,接收玩家的url然后将这个url进行一定处理后请求json数据,将所有数据都爬下来后再进行数据分析,最后返回结果。

首先将接收请求并且解析json的函数做一个封装

    // 接收url,并且将数据解析成实体类列表返回private List<ItemEntity> parsejson(String url){try {// 这里填你自己的urlDocument document = Jsoup.connect(url).header("Accept", "*/*").header("Accept-Encoding", "gzip, deflate").header("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3").header("Content-Type", "application/json;charset=UTF-8").header("Content-Type", "application/x-javascript;charset=UTF-8").header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0").timeout(10000).ignoreContentType(true).get();//System.out.println(document.body().text());String jsonstr = document.body().text();// 将json字符串转为mapMap<String, Object> map1 = (Map<String, Object>) JSON.parse(jsonstr);//System.out.println(map1.get("data"));// 获取data中的list信息Map<String, Object> map2 = (Map<String, Object>) JSON.parse(map1.get("data").toString());List<ItemEntity> itemEntityList = JSON.parseArray(map2.get("list").toString(), ItemEntity.class);/*System.out.println(itemEntityList);for (int i = 0; i < itemEntityList.size(); i++) {System.out.println(itemEntityList.get(i));}*/return itemEntityList;} catch (Exception e){e.printStackTrace();}return null;}

接着我们写一个转换url的函数,这个是最基础的转换,将原始的url转换为可以查询json的url

    // 处理url,将传入的url修改为接收json的urlprivate String getURLDiffType(String url){StringBuilder sb = new StringBuilder();String base = "https://hk4e-api.mihoyo.com/event/gacha_info/api/getGachaLog?";sb.append(base);String attr = url.split("\\?")[1];String[] attrs = attr.split("#")[0].split("&");// 将参数拼装for(int i=0; i<attrs.length; i++){sb.append(attrs[i]);sb.append("&");}return sb.toString();}

然后我们需要一个主启动函数,调用这个函数就可以把四个卡池都遍历一遍,得到所有的数据

经过测试,我发现,接口中page参数不重要,只需要设置end_id和size就行,当end_id为0的时候,就表示从头开始读数据。

我们需要不断修改end_id知道返回的数据为空为止

    // 主启动函数,遍历卡池,得到所有数据private Map<String, List<ItemEntity>> getAllItem(String url){String[] gachatypes = new String[]{"100", "200", "301", "302"};// 定义一个Map用来存放四个卡池的数据Map<String, List<ItemEntity>> map = new HashMap<>();map.put("100", new ArrayList<>());map.put("200", new ArrayList<>());map.put("301", new ArrayList<>());map.put("302", new ArrayList<>());String baseurl = getURLDiffType(url);for(int i=0; i<gachatypes.length; i++){StringBuilder sb = new StringBuilder();sb.append(baseurl);sb.append("gacha_type=" + gachatypes[i] + "&");sb.append("page=1&");sb.append("size=20&");String baseurlplus = sb.toString();String end_id = "0";while(true){StringBuilder str = new StringBuilder();str.append(baseurlplus);str.append("end_id=" + end_id);System.out.println("查询卡池:" + gachatypes[i] + " end_id=" + end_id);List<ItemEntity> itemEntityList = parsejson(str.toString());// 如果查询到的数据为空表示查完了if(itemEntityList.size() == 0){System.out.println("卡池(" + gachatypes[i] + ")查询完毕");break;}// 将数据装到map里for(int j=0; j<itemEntityList.size(); j++){map.get(gachatypes[i]).add(itemEntityList.get(j));}// 得到最后一个元素的end_idend_id = itemEntityList.get(itemEntityList.size()-1).getId();try {// 每个查询需要间隔一段时间Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}return map;}

最后我们可以针对得到的map进行数据分析

    // 对查询到的结果进行后处理操作private void postprocess(Map<String, List<ItemEntity>> ItemEntityMap){// 首先取出四个卡池的信息// 卡池分别是 新手卡池,常驻卡池,活动up卡池和武器卡池List<ItemEntity> kachi1 = ItemEntityMap.get("100");List<ItemEntity> kachi2 = ItemEntityMap.get("200");List<ItemEntity> kachi3 = ItemEntityMap.get("301");List<ItemEntity> kachi4 = ItemEntityMap.get("302");// 统计所有的数量int total_all = kachi1.size() + kachi2.size() + kachi3.size() + kachi4.size();// 统计武器的数量int total_wuqi = 0;// 统计角色的数量int total_juese = 0;// 统计3星数量int total_3star = 0;// 统计4星的数量int total_4star = 0;// 统计5星的数量int total_5star = 0;// 遍历四个卡池得到数据for(int i=0; i<kachi1.size(); i++){ItemEntity item = kachi1.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}for(int i=0; i<kachi2.size(); i++){ItemEntity item = kachi2.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}for(int i=0; i<kachi3.size(); i++){ItemEntity item = kachi3.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}for(int i=0; i<kachi4.size(); i++){ItemEntity item = kachi4.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}// 打印数据System.out.println("所有卡池的总物品数是:" + total_all);System.out.println(String.format("新手卡池抽卡总数为:%d \n常驻卡池抽卡总数为:%d \n活动up卡池抽卡总数为:%d \n武器卡池抽卡总数为:%d",kachi1.size(), kachi2.size(), kachi3.size(), kachi4.size()));System.out.println("总角色数是:" + total_juese + " 抽到角色的概率为:" + String.format("%.4f", (double)total_juese/total_all));System.out.println("总武器数是:" + total_wuqi + " 抽到武器的概率为:" + String.format("%.4f", (double)total_wuqi/total_all));System.out.println("3星的数量是:" + total_3star + " 抽到3星的概率为:" + String.format("%.4f", (double)total_3star/total_all));System.out.println("4星的数量是:" + total_4star + " 抽到4星的概率为:" + String.format("%.4f", (double)total_4star/total_all));System.out.println("3星的数量是:" + total_5star + " 抽到3星的概率为:" + String.format("%.4f", (double)total_5star/total_all));}

总的代码如下

public class MiHaYouSoup {public static void main(String[] args) throws Exception {//String url = "https://hk4e-api.mihoyo.com/event/gacha_info/api/getGachaLog?authkey_ver=1&sign_type=2&auth_appid=webview_gacha&init_type=301&gacha_id=50eef7be3b7945930041f9f20310e0bbcb8a7a&timestamp=1648597804&lang=zh-cn&device_type=mobile&ext=%7b%22loc%22%3a%7b%22x%22%3a-206.83416748046876%2c%22y%22%3a201.1728973388672%2c%22z%22%3a-993.37060546875%7d%2c%22platform%22%3a%22Android%22%7d&game_version=CNRELAndroid2.6.0_R6626848_S6641322_D6692205&plat_type=android&region=cn_gf01&authkey=wR9769ie0pACSLWz2W3ggPLgAe6jwLyaCX8%2f7qE2ywEWqPDFceqGlSewiU7u0%2fS7osSNc%2fyZKtg2LOrM%2fzAsw5W%2bOEKRrtqMC4CNmARgbGag0PC5q071zjwKbQyBx4LpkPjr7%2f%2bkaBLORncy8NcB6IMiqsXDYYot6DmhbytWKPRGH6XHxZOK6avncs1B7dxJYISjv5OuyqIzfQ8fynDtei7EclBKT8GXpdfaPd3M0Dq4QfD9qd8757g%2f4wSPqbxBHwvl5yFUv0k4VQsmladDiEbeseLPDxHzB6VggTijG3UoMGgVLqSlprUAdLEpYDIsOjqMUTa5oywDBUqXF%2fvhAt7fzyx8Cf2orSUjjpJZUEEtJSltDLCJlNUJllysfbo84lOJnNeUEJdPn91D0%2bLN1UG8J7%2fRgmF9TBtbLGHzFXkMk0TGWSkuF%2f1FNKm6VJtNX7K1V4PoPdk6u%2bLbmH3Wz4SR6ZJXCIgNwor6SKrKa0RxtY6skwPuNHlMPTO9FwSo0c1FN75X8iJaNUO5MuZrPVHbLkRoTZdpjFOiRh1R0P2UY%2b5MIR6wErfS6u7kK0Zxyz8n9IUG%2fc2IfHrys0WogXmmikxXQwB8LR9%2b6N%2bB2xvNzr6tRMVIHdamAetaHMM3cXXt2Iy7FiANW2B8gYbW2ts1zvN38Bf4zC6RNfRk2w9oSvKnEUsNA5po9eaBs1YSeBYf1FCj21jRj11K8x%2ffuwRO4zSLEvdYBHp2PJzM56Ddiyxflo5Im8p3HlR1dd8IguzhYySzw7uy%2bw2F00jx9gW%2fDFR5CGKOXil%2bUfOUFsI7V1qg7GX%2fLi3dzJBu6Nt1YXmJZf6SjKuOmUNU7vEBd14MPsc%2bUen80MXUCN6a28GovAixc1IWbcS%2fBhNsxR3ZkSIaMGRLvSssgY91Ah8QJXkUr6zbFZcTXUz4LGIQUmpolI22Nj5wPX5iiDwexLGqQgwn%2fhS5MYuWDAPxeaSOIlqQuVvmva3S8d083E%2bN6XWZLs%2bSS0SZ%2bCnYQmM59G1sNNoQJUwx8krxFoWQVPSASQy4QM6pLXUYYzP2sY%2bm9TD%2bzdWcm4Z%2fe16%2bClAFbCk5Y9rYXgxIshLDUIMjhSlQJkp7rqalYPQu8IewhX1VjDbQ6fEjD%2b8gg6QeBKGytwivPCZJoSKh%2fO7ebEawvTeSVHBvw1M3iztKHB2nk6QxQiGMfh%2b56qu3Tr8KxxqI9Dq1CePwbfIRhHHP7oujqBoJ2xuAY0VCgzRFfUViPdT8gTrHE7QEE6hNi4ZpZvqkQYTTXUW%2fgqvNFYYvmGUDs19g%2b8M0gELlcmNN%2bBUFl4TUUH0n%2fjiAy1CXyMf00wUtuNHfw8smHW9f8ELlLIgQd%2bWsZA%3d%3d&game_biz=hk4e_cn&gacha_type=301&page=5&size=2&end_id=1649513160000977558";String url = "https://webstatic.mihoyo.com/hk4e/event/e20190909gacha/index.html?authkey_ver=1&sign_type=2&auth_appid=webview_gacha&init_type=301&gacha_id=50eef7be3b7945930041f9f20310e0bbcb8a7a&timestamp=1648597804&lang=zh-cn&device_type=mobile&ext=%7b%22loc%22%3a%7b%22x%22%3a-206.83416748046876%2c%22y%22%3a201.1728973388672%2c%22z%22%3a-993.37060546875%7d%2c%22platform%22%3a%22Android%22%7d&game_version=CNRELAndroid2.6.0_R6626848_S6641322_D6692205&plat_type=android&region=cn_gf01&authkey=wR9769ie0pACSLWz2W3ggPLgAe6jwLyaCX8%2f7qE2ywEWqPDFceqGlSewiU7u0%2fS7osSNc%2fyZKtg2LOrM%2fzAsw5W%2bOEKRrtqMC4CNmARgbGag0PC5q071zjwKbQyBx4LpkPjr7%2f%2bkaBLORncy8NcB6IMiqsXDYYot6DmhbytWKPRGH6XHxZOK6avncs1B7dxJYISjv5OuyqIzfQ8fynDtei7EclBKT8GXpdfaPd3M0Dq4QfD9qd8757g%2f4wSPqbxBHwvl5yFUv0k4VQsmladDiEbeseLPDxHzB6VggTijG3UoMGgVLqSlprUAdLEpYDIsOjqMUTa5oywDBUqXF%2fvhAt7fzyx8Cf2orSUjjpJZUEEtJSltDLCJlNUJllysfbo84lOJnNeUEJdPn91D0%2bLN1UG8J7%2fRgmF9TBtbLGHzFXkMk0TGWSkuF%2f1FNKm6VJtNX7K1V4PoPdk6u%2bLbmH3Wz4SR6ZJXCIgNwor6SKrKa0RxtY6skwPuNHlMPTO9FwSo0c1FN75X8iJaNUO5MuZrPVHbLkRoTZdpjFOiRh1R0P2UY%2b5MIR6wErfS6u7kK0Zxyz8n9IUG%2fc2IfHrys0WogXmmikxXQwB8LR9%2b6N%2bB2xvNzr6tRMVIHdamAetaHMM3cXXt2Iy7FiANW2B8gYbW2ts1zvN38Bf4zC6RNfRk2w9oSvKnEUsNA5po9eaBs1YSeBYf1FCj21jRj11K8x%2ffuwRO4zSLEvdYBHp2PJzM56Ddiyxflo5Im8p3HlR1dd8IguzhYySzw7uy%2bw2F00jx9gW%2fDFR5CGKOXil%2bUfOUFsI7V1qg7GX%2fLi3dzJBu6Nt1YXmJZf6SjKuOmUNU7vEBd14MPsc%2bUen80MXUCN6a28GovAixc1IWbcS%2fBhNsxR3ZkSIaMGRLvSssgY91Ah8QJXkUr6zbFZcTXUz4LGIQUmpolI22Nj5wPX5iiDwexLGqQgwn%2fhS5MYuWDAPxeaSOIlqQuVvmva3S8d083E%2bN6XWZLs%2bSS0SZ%2bCnYQmM59G1sNNoQJUwx8krxFoWQVPSASQy4QM6pLXUYYzP2sY%2bm9TD%2bzdWcm4Z%2fe16%2bClAFbCk5Y9rYXgxIshLDUIMjhSlQJkp7rqalYPQu8IewhX1VjDbQ6fEjD%2b8gg6QeBKGytwivPCZJoSKh%2fO7ebEawvTeSVHBvw1M3iztKHB2nk6QxQiGMfh%2b56qu3Tr8KxxqI9Dq1CePwbfIRhHHP7oujqBoJ2xuAY0VCgzRFfUViPdT8gTrHE7QEE6hNi4ZpZvqkQYTTXUW%2fgqvNFYYvmGUDs19g%2b8M0gELlcmNN%2bBUFl4TUUH0n%2fjiAy1CXyMf00wUtuNHfw8smHW9f8ELlLIgQd%2bWsZA%3d%3d&game_biz=hk4e_cn#/log";MiHaYouSoup miHaYouSoup = new MiHaYouSoup();//System.out.println(miHaYouSoup.getURLDiffType(url));Map<String, List<ItemEntity>> ItemEntityMap = miHaYouSoup.getAllItem(url);//System.out.println(ItemEntityMap);miHaYouSoup.postprocess(ItemEntityMap);}// 对查询到的结果进行后处理操作private void postprocess(Map<String, List<ItemEntity>> ItemEntityMap){// 首先取出四个卡池的信息// 卡池分别是 新手卡池,常驻卡池,活动up卡池和武器卡池List<ItemEntity> kachi1 = ItemEntityMap.get("100");List<ItemEntity> kachi2 = ItemEntityMap.get("200");List<ItemEntity> kachi3 = ItemEntityMap.get("301");List<ItemEntity> kachi4 = ItemEntityMap.get("302");// 统计所有的数量int total_all = kachi1.size() + kachi2.size() + kachi3.size() + kachi4.size();// 统计武器的数量int total_wuqi = 0;// 统计角色的数量int total_juese = 0;// 统计3星数量int total_3star = 0;// 统计4星的数量int total_4star = 0;// 统计5星的数量int total_5star = 0;// 遍历四个卡池得到数据for(int i=0; i<kachi1.size(); i++){ItemEntity item = kachi1.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}for(int i=0; i<kachi2.size(); i++){ItemEntity item = kachi2.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}for(int i=0; i<kachi3.size(); i++){ItemEntity item = kachi3.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}for(int i=0; i<kachi4.size(); i++){ItemEntity item = kachi4.get(i);if(item.getItem_type().equals("武器")){total_wuqi++;}if(item.getItem_type().equals("角色")){total_juese++;}if(item.getRank_type().equals("3")){total_3star++;}if(item.getRank_type().equals("4")){total_4star++;}if(item.getRank_type().equals("5")){total_5star++;}}// 打印数据System.out.println("所有卡池的总物品数是:" + total_all);System.out.println(String.format("新手卡池抽卡总数为:%d \n常驻卡池抽卡总数为:%d \n活动up卡池抽卡总数为:%d \n武器卡池抽卡总数为:%d",kachi1.size(), kachi2.size(), kachi3.size(), kachi4.size()));System.out.println("总角色数是:" + total_juese + " 抽到角色的概率为:" + String.format("%.4f", (double)total_juese/total_all));System.out.println("总武器数是:" + total_wuqi + " 抽到武器的概率为:" + String.format("%.4f", (double)total_wuqi/total_all));System.out.println("3星的数量是:" + total_3star + " 抽到3星的概率为:" + String.format("%.4f", (double)total_3star/total_all));System.out.println("4星的数量是:" + total_4star + " 抽到4星的概率为:" + String.format("%.4f", (double)total_4star/total_all));System.out.println("3星的数量是:" + total_5star + " 抽到3星的概率为:" + String.format("%.4f", (double)total_5star/total_all));}// 主启动函数,遍历卡池,得到所有数据private Map<String, List<ItemEntity>> getAllItem(String url){String[] gachatypes = new String[]{"100", "200", "301", "302"};// 定义一个Map用来存放四个卡池的数据Map<String, List<ItemEntity>> map = new HashMap<>();map.put("100", new ArrayList<>());map.put("200", new ArrayList<>());map.put("301", new ArrayList<>());map.put("302", new ArrayList<>());String baseurl = getURLDiffType(url);for(int i=0; i<gachatypes.length; i++){StringBuilder sb = new StringBuilder();sb.append(baseurl);sb.append("gacha_type=" + gachatypes[i] + "&");sb.append("page=1&");sb.append("size=20&");String baseurlplus = sb.toString();String end_id = "0";while(true){StringBuilder str = new StringBuilder();str.append(baseurlplus);str.append("end_id=" + end_id);System.out.println("查询卡池:" + gachatypes[i] + " end_id=" + end_id);List<ItemEntity> itemEntityList = parsejson(str.toString());// 如果查询到的数据为空表示查完了if(itemEntityList.size() == 0){System.out.println("卡池(" + gachatypes[i] + ")查询完毕");break;}// 将数据装到map里for(int j=0; j<itemEntityList.size(); j++){map.get(gachatypes[i]).add(itemEntityList.get(j));}// 得到最后一个元素的end_idend_id = itemEntityList.get(itemEntityList.size()-1).getId();try {// 每个查询需要间隔一段时间Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}return map;}// 处理url,将传入的url修改为接收json的urlprivate String getURLDiffType(String url){StringBuilder sb = new StringBuilder();String base = "https://hk4e-api.mihoyo.com/event/gacha_info/api/getGachaLog?";sb.append(base);String attr = url.split("\\?")[1];String[] attrs = attr.split("#")[0].split("&");// 将参数拼装for(int i=0; i<attrs.length; i++){sb.append(attrs[i]);sb.append("&");}return sb.toString();}// 接收url,并且将数据解析成实体类列表返回private List<ItemEntity> parsejson(String url){try {// 这里填你自己的urlDocument document = Jsoup.connect(url).header("Accept", "*/*").header("Accept-Encoding", "gzip, deflate").header("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3").header("Content-Type", "application/json;charset=UTF-8").header("Content-Type", "application/x-javascript;charset=UTF-8").header("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0").timeout(10000).ignoreContentType(true).get();//System.out.println(document.body().text());String jsonstr = document.body().text();// 将json字符串转为mapMap<String, Object> map1 = (Map<String, Object>) JSON.parse(jsonstr);//System.out.println(map1.get("data"));// 获取data中的list信息Map<String, Object> map2 = (Map<String, Object>) JSON.parse(map1.get("data").toString());List<ItemEntity> itemEntityList = JSON.parseArray(map2.get("list").toString(), ItemEntity.class);/*System.out.println(itemEntityList);for (int i = 0; i < itemEntityList.size(); i++) {System.out.println(itemEntityList.get(i));}*/return itemEntityList;} catch (Exception e){e.printStackTrace();}return null;}}

运行结果

查询卡池:100 end_id=0
查询卡池:100 end_id=1646471160001247058
卡池(100)查询完毕
查询卡池:200 end_id=0
查询卡池:200 end_id=1648631160003490058
查询卡池:200 end_id=1646481960001005358
查询卡池:200 end_id=1646481960001004558
卡池(200)查询完毕
查询卡池:301 end_id=0
查询卡池:301 end_id=1649070360000868658
查询卡池:301 end_id=1648609560028066458
查询卡池:301 end_id=1647774360000571358
查询卡池:301 end_id=1646834760003882458
卡池(301)查询完毕
查询卡池:302 end_id=0
卡池(302)查询完毕
所有卡池的总物品数是:132
新手卡池抽卡总数为:10
常驻卡池抽卡总数为:48
活动up卡池抽卡总数为:74
武器卡池抽卡总数为:0
总角色数是:10 抽到角色的概率为:0.0758
总武器数是:122 抽到武器的概率为:0.9242
3星的数量是:117 抽到3星的概率为:0.8864
4星的数量是:15 抽到4星的概率为:0.1136
3星的数量是:0 抽到3星的概率为:0.0000

可以看到我抽卡还是挺非的哈哈。当然,在数据处理的部分,我只是做了一个简单的案例,你可以根据你的需求进行其他的分析。如果把这个功能集成到服务器里,就可以做一个提供查询接口的网站或者微信公众号啦,然后可以对所有进行查询的数据进行排名,看看谁是欧皇,谁是非酋。

总结

本文是java爬虫的一个小案例,相信可以给你提供帮助。

最后,就用蒙德最强战神派蒙来结束本文吧

我用java分析了原神抽卡记录相关推荐

  1. java 做的原神抽卡模拟小程序

    java做的原神抽卡模拟小程序 难度不大,适合学完面向对象后做着玩. import java.util.ArrayList; import java.util.Calendar; import jav ...

  2. 原神抽卡记录分析工具源码全开源

    简介: 原理是读取AppData\LocalLow\miHoYo\原神\output_log.txt这个文件中的链接,具体看源码,都给做了详细注释. 网盘下载地址: http://kekewangLu ...

  3. JAVA编译一个原神抽卡模拟器

    import java.util.Random;import java.util.Scanner;public class Chouka {final static String[] sanxingw ...

  4. 原神抽卡模拟器(java简易版)

    原神抽卡机制 单抽概率: 5★物品:0.6% 4★物品:5.1% 保底机制: 5★保底:如果连续89发没出5星,第90抽必定5星,然后重新计数 4★保底:如果连续9发没出4星,第10抽触发4星保底:0 ...

  5. java原神抽卡器(可查询版本)

    原神官方给的概率: 5星基础:0.6%,5星保底:1.6%,90抽5星保底:         4星基础:5.1%,4星保底:13%,10抽4星保底. 一开始我也想着直接用官方给的概率设置直接写入,但发 ...

  6. 原神抽卡模拟器,unity制作(由于没有获得作者的视频授权,不会发布软件,只展示算法与开发等,效果图在个人主页类有资源下载,不会上传视频)

    五星效果图 以上为展示,没做优化 using System.Collections; using System.Collections.Generic; using UnityEngine; usin ...

  7. 原神抽卡模拟简单代码(概率还原)

    代码如下: import java.util.Scanner; import java.util.Random;public class Main {public static void main(S ...

  8. Python实现原神抽卡,生成桌面程序,tkinter

    这里写自定义目录标题 话不多说,直接贴所有代码 运行效果 需要用到的两张图片 话不多说,直接贴所有代码 import random import sys import tkinter as tk # ...

  9. 原神抽卡(题目出自:江西软件职业技术大学)

    描述: 在终于熬过了高中之后,你进入了大学,你听信了大人们的谎言,上了大学就轻松了,实际上你发现大学比高中更卷了.但是!你已经佛系了起来,凭借着高中学过oi,在大学开始了摸鱼,而一直打LOL的你,最近 ...

最新文章

  1. Linux课程---7、shell技巧(获取帮助命令)
  2. 【五线谱】拍号与音符时值 ( 五线谱拍号 | 全音符休止符 | 二分音符休止符 | 四分音符休止符 | 八分音符休止符 | 十六分音符休止符 | 三十二分音符休止符 )
  3. input file 获取不到Request.Files 解决办法
  4. JavaScript MVC之Jamal
  5. rstudio python_如虎添翼:用Python与C++扩展R语言的应用场景
  6. POP3口令扫描案例
  7. spring tiles_Spring MVC 3模板和Apache Tiles
  8. mysql 酒店管理设计_酒店管理系统的设计与实现(Myeclipse,MySQL)
  9. xshell怎么让程序后台运行_使程序在Linux下后台运行
  10. WebStorm:常用插件(全部实用)包含主题、界面、开发效率等
  11. matlab 流固耦合,流固耦合的研究与发展综述
  12. Qt截取长图(带滚动条被遮挡部分需要展开截取全图)QScrollArea、QPixmap
  13. postman:Tests模块之断言
  14. JAVA对接飞猪旅行_飞猪对接教程
  15. 一套OA系统需要多少钱?
  16. 四大国际反垃圾邮件组织介绍
  17. linux使用入门debian,Debian 7.7入门安装与配置
  18. 计算机网络技术期末复习考点
  19. BZOJ 2069: [POI2004]ZAW(Dijkstra + 二进制拆分)
  20. 家装灯线走线图_二十年资深装修电工是如何布线的?家庭装修电工布线图详解...

热门文章

  1. 支付宝微信合单支付对接说明
  2. 光耦继电器工作原理与参数详解
  3. [享学Eureka] 十九、远程通信模块:EurekaHttpClients工具快速构建ClusterResolver集群解析器
  4. Getting Icon Overlays to Work
  5. java和设计模式(结构模式)
  6. 华为手机视频解码首屏时延比较大的问题
  7. Ubuntu 18.04 disable ipv6
  8. IT痴汉的工作现状52-神队友与猪队友
  9. 怎样选择青少年护眼灯?儿童护眼灯五大品牌推荐
  10. 2020,华为平板市场份额超过苹果平板将是大概率事件