云顶之奕S2上线了,回顾两个赛季,有哪些暂时失宠的英雄们还未曾登场呢?作为程序猿,你让咱一个个对照数出来,是不存在的,不如爬取一下数据,分析一波,既有趣,又锻炼了编程,岂不快哉!
新建maven项目,导入jsoup与httpclient与jackson等依赖。
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!-- httpClient --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><!-- Jsoup --><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.8.3</version></dependency><!-- 工具包 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.5</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.3</version></dependency></dependencies>
登录lol官网 https://lol.qq.com/act/a20190920worlds/index.shtml?e_code=491407&idataid=278057
1.获取全部英雄数据
点击游戏资料,里边会有lol全部英雄信息。打开浏览器F12分析,英雄是从“https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js” 获取的json数据。
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;@SuppressWarnings("static-access")
public class GetAllHero {private static PoolingHttpClientConnectionManager cm;public GetAllHero() {this.cm = new PoolingHttpClientConnectionManager();
// 设置最大连接数this.cm.setMaxTotal(100);
// 设置每个主机最大连接数this.cm.setDefaultMaxPerRoute(10);}public static void main(String[] args) throws Exception {// 开启爬虫getAllHeros();}public static HashSet<String> getAllHeros() throws Exception {//解析地址String url = "https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js"; //首页String html = doGetHtml(url);HashSet<String> allHeroSet = parse(html);return allHeroSet;}/*** 根据请求地址下载页面数据** @param url* @return 页面数据*/public static String doGetHtml(String url) {
// 获取httpClient对象CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
// 创建httpGet请求对象,设置url地址HttpGet httpGet = new HttpGet(url);httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36");
// 设置请求信息httpGet.setConfig(getConfig());
// 使用httpClient发起请求,获取响应CloseableHttpResponse response = null;try {response = httpClient.execute(httpGet);
// 解析响应,返回结果if (response.getStatusLine().getStatusCode() == 200) {
// 判断响应体Entity是否不为空,如果不为空,就可以使用Entityutilsif (response.getEntity() != null) {String content = EntityUtils.toString(response.getEntity(), "UTF-8");return content;}}} catch (IOException e) {e.printStackTrace();} finally {
// 关闭responseif (response != null) {try {response.close();} catch (IOException e) {e.printStackTrace();}}}
// 解析响应,返回结果return "";}@SuppressWarnings("unchecked")private static HashSet<String> parse(String json) throws Exception {HashSet<String> allHeroSet = new HashSet<String>();HashMap<String, Object> herosMap = new HashMap<String, Object>();ObjectMapper mapper = new ObjectMapper();herosMap = (HashMap<String, Object>) mapper.readValue(json, Map.class);List<Map<String, Object>> herosList = (List<Map<String, Object>>) herosMap.get("hero");for (Map<String, Object> map : herosList) {allHeroSet.add((map.get("name").toString().trim()+map.get("title").toString().trim()));}return allHeroSet;}// 设置请求信息private static RequestConfig getConfig() {RequestConfig config = RequestConfig.custom().setConnectTimeout(1000).setConnectionRequestTimeout(500).setSocketTimeout(10000) //数据传输的最长时间.build();return config;}
}
这时,我获取到的所有的英雄名字和称号都放到了allHeroSet中去了,为啥要用set?为啥要把名字和称号都获取到?请接着往下看。
2.获取云顶之奕牌数据
打开云顶之奕的资料,F12 ---> 网络中找到:
TF开头的都是云顶之奕的资料,其中V3是新赛季的资料。
这样,有了url,就可以获取到英雄的json数据了。点击"TFTHerosData.js",这里边是云顶之奕两个赛季的全部牌的英雄数据:
如图,获取到的这些就是unicode编码的英雄的json数据。将这些数据copy出来,(开头的function.....不要,只要function返回的数据)用网上的在线json解析工具格式化一下,然后将格式化好的json串copy到一个文件中, 我们通过读取文件的方式解析 数据。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;/*** 得到云顶所有英雄的数据**/
public class GetYdHero {public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {getYdHeros();}@SuppressWarnings("unchecked")public static HashSet<String> getYdHeros() throws IOException{HashSet<String> heroSet = new HashSet<String>();File allYdHeroJson = new File("D:\\Users\\zzh\\eclipse-workspace\\crawler\\resources/allYdHero.json");BufferedReader bufferedReader = new BufferedReader(new FileReader(allYdHeroJson));String line = null;StringBuilder result = new StringBuilder();while ((line = bufferedReader.readLine()) != null) {result.append(line);}HashMap<String, Object> herosMap = new HashMap<String, Object>();ObjectMapper mapper = new ObjectMapper();herosMap = (HashMap<String, Object>) mapper.readValue(result.toString(),Map.class);for (Entry<String, Object> e : herosMap.entrySet()) {Map<String, Object> heroData = (Map<String, Object>) e.getValue();for (Entry<String, Object> a : heroData.entrySet()) {if("hero_tittle".equals(a.getKey())) {heroSet.add(a.getValue().toString().trim());}}}return heroSet;}}
以上为什么要用set呢,因为set中不允许有重复的值,云顶之奕新版本中,大元素使拉克丝由于羁绊不固定,所以她出现了7次,琪亚娜出现了四次,为了避免重复,用set存储遍历出来的数据要好处理一些。
另外,我发现获取到的云顶的数据中,有时候会把英雄的名字与称号搞错,例如,丽桑卓是名字hero_name,但是获取到的数据中却把名字放到了hero-tittle中去了。(这个tittle是不是搞错了?明明是title,看来lol官方程序猿英语也不咋地?)总之,就是名字称号整的很乱,这为接下来的分析数据带来了困扰,于是我干脆只把hero_tittle存储到了set中去了。
3.做分析
新建一个比较类,
import java.util.HashSet;/*** 比较云顶之奕英雄与lol全英雄* * @author zzh**/
public class CompareHeros {public static void main(String[] args) throws Exception {compare();}private static void compare() throws Exception {// 得到全英雄HashSet<String> allHeros = GetAllHero.getAllHeros();// 得到云顶英雄HashSet<String> ydHeros = GetYdHero.getYdHeros();int j = 0;//计算未出场的英雄for (String allHero : allHeros) {int i = 0;for (String ydHero : ydHeros) {if (allHero.contains(ydHero)) {
// System.out.println(allHero); 云顶之奕出场的英雄break;}++i;}if(i==ydHeros.size()) {System.out.println(allHero+ ++j);}}}}
这是直接双重循环遍历两个个set,由于我allHero获取到的是名字+称号,例如赏金猎人厄运小姐,而云顶的英雄获取到的不一定是名字还是称号,可能是赏金猎人,也可能是厄运小姐。所以我用字符串的包含函数contains,如果包含返回true,不包含,返回false.
如果某英雄云顶未曾出现,如龙龟,则内循环会循环的次数是ydHero.size()次,把这个当作判断条件,把未曾出来的英雄打印出来即可。
最后结果未曾上线的云顶英雄:
发条魔灵奥莉安娜1
大发明家黑默丁格2
德邦总管赵信3
符文法师瑞兹4
殇之木乃伊阿木木5
复仇之矛卡莉丝塔6
德玛西亚之翼奎因7
巨魔之王特朗德尔8
天启者卡尔玛9
无畏战车厄加特10
恶魔小丑萨科11
影流之镰凯隐12
解脱者塞拉斯13
雪原双子努努和威朗普14
刀锋舞者艾瑞莉娅15
河流之王塔姆16
涤魂圣枪赛娜17
暴怒骑士克烈18
正义巨像加里奥19
英勇投弹手库奇20
披甲龙龟拉莫斯21
星界游神巴德22
琴瑟仙女娑娜23
齐天大圣孙悟空24
戏命师烬25
远古巫灵泽拉斯26
机械先驱维克托27
海兽祭司俄洛伊28
迅捷斥候提莫29
暮光星灵佐伊30
战争之影赫卡里姆31
蛮族之王泰达米尔32
刀锋之影泰隆33
牛头酋长阿利斯塔34
潮汐海灵菲兹35
逆羽霞36
放逐之刃锐雯37
魔法猫咪悠米38
幻翎洛39
德玛西亚皇子嘉文四世40
酒桶古拉加斯41
爆破鬼才吉格斯42
时间刺客艾克43
魔蛇之拥卡西奥佩娅44
皮城女警凯特琳45
机械公敌兰博46
末日使者费德提克47
兽灵行者乌迪尔48
生化魔人扎克49
虚空之眼维克兹50
盲僧李青51
时光守护者基兰52
展望:通过以上我的分析,其实lol官网中能获取到的的数据有很多,如羁绊信息,装备信息等等,都在TF开头的url中,我写这篇也只是抛砖引玉,如果您有兴趣,不妨把这些数据加以入库,分析等等操作,也可以计算完美羁绊、计算有多少种羁绊等等。
动手试试吧。
源码放在了github:“https://github.com/Upzzh/analyseYDZYHeros”中。