Bootstrap

【Java项目】讲讲我用Java爬虫获取LOL英雄数据与图片(附源码)_游戏数据抓取(3)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
img

正文

  <artifactId>hutool-all</artifactId>
  <version>5.8.9</version>
</dependency>

#### 3.2.工具介绍(hutool)



> 
> 这是一个包含各大常用工具方法的Java工具包。
> 
> 
> 里面具有丰富的工具资源,可以让你省下不少写一些通用方法的时间。
> 
> 
> Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug
> 
> 
> **官方传送门**:[Hutool官方地址]( )
> 
> 
> 可以在官方地址中查看对应的文档信息。
> 
> 
> 这次主要我们要用到Hutool工具中对`文件、请求`的处理的工具!
> 
> 
> 


##### 🎁Hutool名称的由来


Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu”是公司名称的表示,tool表示工具。Hutool谐音“糊涂”,一方面简洁易懂,一方面寓意“难得糊涂”。


#### 3.3.初窥门径



> 
> 我们进行爬虫的第一步就是先明确的我们的需求。
> 
> 
> 首先我们应当想办法找到`LOL`英雄资料的页面(URL),然后我们才开始解析我们的页面。
> 
> 
> 🚪 [LOL官方英雄资料地址]( )
> 
> 
> 


**学会解析页面**



> 
> 首先我们要鼠标右键`查看网页源代码`
> 
> 
> 


![image-20221114224920149](https://md-img-1313456957.cos.ap-guangzhou.myqcloud.com/img%2F202211142249786.png)

> 
> 由此我们可以发现我们的页面数据不过是寥寥几十行,并没有我们所需要的英雄数据信息,说明当前网站的页面信息并不是静态的。而是动态的(这里需要大家对HTTP请求有一定的基础知识)
> 
> 
> 因为数据是动态的所以我们需要去查看当前页面的请求链接去分析和寻找(这里需要耐心一点)
> 
> 
> 



> 
> 打开官网地址我们需要按下`F12`然后选择`Network`
> 
> 
> 刷新一下来查看当前网页的数据源是怎么展示在页面上的请求链接,我们可以发现一个比较可疑的链接`hero_list.js`
> 
> 
> 从名字来看是英雄列表的意思,只要你点进去查看返回结果可以发现这个就是我们想要的信息。
> 
> 
> 


![image-20221114225457022](https://img-blog.csdnimg.cn/img_convert/10897815fd24d62c0e88e387c687e144.png)



> 
> 由此分析我们就得到了当前英雄信息的数据源,我们接下来要做的就是去处理这个数据。
> 
> 
> 我们得到了我们要请求的路径为:https://game.gtimg.cn/images/lol/act/img/js/heroList/hero\_list.js?ts=2780729
> 
> 
> 


#### 3.4.利用WebMagic处理请求



> 
> 得到我们的需要的数据源之后,我们就要学会利用我们的爬虫来往数据源里面进行爬取数据。
> 
> 
> 对于数据的处理我们是通过自定义`PageProcessor`来完成的,所以我们需要创建一个类来实现这个接口。
> 
> 
> 并且重写我们的`process()`核心函数,改为我们要做的事情。
> 
> 
> 


**简单梳理一下我们要做的事情**


1. 判断请求路径是否与我们的目标路径一致
2. 路径如果一致则获取文件中的`json`数据转换成Java对象
3. 将处理好变成Java对象的数据存到`page`对象中等待`Pipeline`的处理
4. `Pipeline`对象在接收到处理好传递过来的数据的时候,进行收尾工作,可以选择`保存文件、输出在控制台`等等



> 
> 博主在代码中也对内容步骤进行了注释,可以进行参考查看。
> 
> 
> 


**核心处理代码**



import cn.dengsz.common.Constants;
import cn.dengsz.common.model.HeroInfo;
import cn.dengsz.common.model.HeroSkin;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.processor.PageProcessor;

import java.util.List;
import java.util.stream.Collectors;

/**
* @author Deng’s
* 去获取页面的进程
*/

public class HerosListPageProcessor implements PageProcessor {

/\*\*

* 核心程序部分
*/
@Override
public void process(Page page) {
// 处理英雄列表信息
if (page.getUrl().get().equals(Constants.HERO_URL)) {
// 获取页面内容
String jsonResult = page.getJson().toString();
// 利用fastjson解析json内容(根据返回内容决定获取key:hero的内容)
JSONObject jsonObject = JSONObject.parseObject(jsonResult);
// 将内容转换成数组
JSONArray heros = jsonObject.getJSONArray(Constants.HERO_KEY);
// 版本信息、更新时间
String version = jsonObject.getString(Constants.VERSION);
String updateFileTime = jsonObject.getString(Constants.UPDATE_TIME);
// 获取到数据数组 判断数组内容是否为null
if (heros.size() == 0) {
return;
}
// 将处理好的信息存入Pipeline中
List heroInfoList = heros.toJavaList(HeroInfo.class);
page.putField(Constants.HERO_KEY, heroInfoList);
page.putField(Constants.VERSION, version);
page.putField(Constants.UPDATE_TIME, updateFileTime);

    }
}

@Override
public Site getSite() {
    // 设置相关的请求头信息,防止反爬虫或者无效访问被拒绝
    return Site.me().setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10\_15\_7) AppleWebKit/" +
                    "537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36")
            .addHeader("accept-encoding", "gzip, deflate, br")
            .addHeader("accept-language", "zh-CN,zh;q=0.9,en;q=0.8")
            .addHeader("origin", "https://101.qq.com")
            .setCharset("utf-8")
            .setRetryTimes(3).setSleepTime(1000);
}

}


**英雄信息的对象模型**



import lombok.Data;
/**
* @author Deng’s
* 仅仅获取一些有用的相关数据 保存下来。
*/
@Data
public class HeroInfo {

/\*\*

* 英雄id
*/
private String heroId;

/\*\*

* 中文名
*/
private String name;

/\*\*

* 别名
*/
private String alias;

/\*\*

* 信息标题
*/
private String title;

/\*\*

* 金币售价
*/
private String goldPrice;

/\*\*

* 点券售价
*/
private String couponPrice;

/\*\*

* 一些关键信息
*/
private String keywords;

}


**一些固定的常量**



> 
> 写一些常量方便之后需要改动的时候进行全局直接生效。
> 
> 
> 比如`文件存储位置、初始访问链接、固定常量名`等等
> 
> 
> 



/**
* @author Deng’s
* 一些解析数据的常量
*/

public class Constants {
public static final String HERO_KEY = “hero”;
public static final String VERSION = “version”;
public static final String UPDATE_TIME = “fileTime”;
public static final String PIC_URL = “https://game.gtimg.cn/images/lol/act/img/js/hero/”;
public static final String HERO_URL = “https://game.gtimg.cn/images/lol/act/img/js/heroList/hero_list.js?ts=2780565”;

/\*\*

* 预设一些文件存储地址
* 英雄信息文件、英雄图片文件存储路径(默认桌面)
*/
public static final String HERO_INFO_FILE = “/Users/dengs/Desktop/lol-skins/hero.json”;
public static final String HERO_PIC_FILE = “/Users/dengs/Desktop/lol-skins/”;
}


**核心的自定义Pipeline类**



> 
> 本教程将英雄数据存储为本地的`json`文件,存储地址可以去改动`Constants`类中的`HERO_INFO_FILE`常量值来改变。
> 
> 
> 



import cn.dengsz.common.Constants;
import cn.dengsz.common.model.HeroInfo;
import cn.dengsz.common.model.HeroSkin;
import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.pipeline.Pipeline;

import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

/**
* @author Deng’s
* 处理由pageProcessor处理好后 塞过来的英雄数据(当然你可以在这里改造成存入数据库)
*/
@Slf4j
public class LolHeroPipeline implements Pipeline {

@Override
public void process(ResultItems resultItems, Task task) {
    // 判断当前请求路径是什么 再决定做什么事情
    if (resultItems.getRequest().getUrl().equals(Constants.HERO\_URL)) {
        // 根据Processor传递过来参数做下一步处理
        List<HeroInfo> heroInfoList = resultItems.get(Constants.HERO\_KEY);
        // 利用hutool可以将内容快速输出成文件
        try {
            //Constants.HERO\_INFO\_FILE 为文件输出的地址
            FileWriter fileWriter = new FileWriter(Constants.HERO\_INFO\_FILE);
            fileWriter.write(JSONObject.toJSONString(heroInfoList));
            fileWriter.close();
        } catch (IOException e) {
            log.error("写出英雄信息出现问题,请查看:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }
}

}


**最后放上这个项目的启动类**



import cn.dengsz.common.Constants;
import cn.dengsz.core.HerosListPageProcessor;
import cn.dengsz.core.LolHeroPipeline;
import us.codecraft.webmagic.Spider;

/**
* @author Deng’s
*/
public class App
{

public static void main( String[] args ) {
    // 调用数据爬虫进程
    // 可以增加线程来提高运行效率(thread)
    long beginTime = System.currentTimeMillis();
    Spider.create(new HerosListPageProcessor())
            .addUrl(Constants.HERO\_URL)
            .addPipeline(new LolHeroPipeline())
            .thread(5)
            .run();
    System.out.printf("用时 %d ms",System.currentTimeMillis()-beginTime);
}

}



> 
> 总结
> 
> 
> 通过以上代码我们可以初步完成对于LOL英雄数据信息的爬取与保存,也算是为大家对爬虫有了初步了解。
> 
> 
> 


### 4.项目实战(提升篇)



> 
> 有了上面的案例以后,其实获取英雄图片也是同理分析和完成的。
> 
> 
> 首先第一步,仍然是对于网页请求的分析。思考一下我们英雄的图片从何而来。
> 
> 
> 


#### 4.1.请求分析



> 
> 我们可以随机点击一个英雄信息后,对英雄的信息进行查看与分析。
> 
> 
> 🚪 [英雄详情页传送门]( )
> 
> 
> 


我们同样通过`F12`在请求列表中找到了一个比较可疑的请求`1.js`


为什么会找到这个请求呢?



> 
> 只要仔细观察会发现我们解析的英雄详情路径是这样的:
> 
> 
> `https://101.qq.com/#/hero-detail?heroid=1&datatype=5v5`
> 
> 
> 可以看到其中有一个关键信息是`heroid=1`
> 
> 
> 不用多说基本就代表了当前英雄的id编号了
> 
> 
> 所以由此才会去找到一个`1.js`的这个请求,与heroId相对应
> 
> 
> 


![image-20221114232335572](https://img-blog.csdnimg.cn/img_convert/5ae2d16a5b9a8382b122a11b81cb240e.png)



> 
> 不出所料我们找到了有用的皮肤信息
> 
> 
> 点开皮肤`skins`查看每个`skin`的数据结构大概如下
> 
> 
> 



{
“skinId”: “1000”,
“heroId”: “1”,
“heroName”: “黑暗之女”,
“heroTitle”: “安妮”,
“name”: “黑暗之女”,
“chromas”: “0”,
“chromasBelongId”: “0”,
“isBase”: “1”,
“emblemsName”: “base”,
“description”: “”,
“mainImg”: “https://game.gtimg.cn/images/lol/act/img/skin/big1000.jpg”,
“iconImg”: “https://game.gtimg.cn/images/lol/act/img/skin/small1000.jpg”,
“loadingImg”: “https://game.gtimg.cn/images/lol/act/img/skinloading/1000.jpg”,
“videoImg”: “https://game.gtimg.cn/images/lol/act/img/skinvideo/sp1000.jpg”,
“sourceImg”: “https://game.gtimg.cn/images/lol/act/img/sourceImg/guide1000.jpg”,
“vedioPath”: “”,
“suitType”: “”,
“publishTime”: “”,
“chromaImg”: “”
}



> 
> 我们可以看到这里面就包含了图片的请求地址`mainImg`
> 
> 
> 当然如果我们继续仔细查看我们就会发现一些炫彩皮肤是没有`mainImg`这个属性值的。
> 
> 
> 所以我们可以在代码处理的时候通过`mainImg`是否有值来判断是不是皮肤,还是炫彩皮肤。
> 
> 
> 所以爬虫的时候 分析是很重要的一件事情,请大家铭记。
> 
> 
> 并且获取到了当前内容的请求地址为:`https://game.gtimg.cn/images/lol/act/img/js/hero/1.js?ts=2780731`
> 
> 
> 


综合上面所有的分析内容,我们就可以知道请求英雄详情的地址是固定的,唯一的变数是结尾的`{heroId}.js`


所以我们只要一一对每个英雄所对应的`heroId`进行拼接访问,再依次获取对应皮肤图片的地址下载下来即可!


#### 4.2.代码部分


**信息对象模型**



> 
> 同样的这样的内容我们需要建立一个对象模型来方便我们接受处理数据
> 
> 
> 



package cn.dengsz.common.model;

import lombok.Data;

/**
* @author Deng’s
* 英雄的皮肤信息实体类(这里的内容可以根据返回的json信息自己进行需要的属性定义)
*/
@Data
public class HeroSkin {

/\*\*

* 皮肤id
*/
private String skinId;

/\*\*

* 英雄id
*/
private String heroId;

/\*\*

* 英雄名
*/
private String heroName;

/\*\*

* 皮肤名
*/
private String name;

/\*\*

* 主图
*/
private String mainImg;

/\*\*

* 图标
*/
private String iconImg;

/\*\*

* 炫彩皮肤
*/
private String chromaImg;
}


**核心处理类HerosListPageProcessor**



> 
> 因为增加了对于英雄id的记录,以及对每次链接请求的判断(如果是列表就保存英雄数据,如果是英雄详情则下载皮肤图片)
> 
> 


**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)**
![img](https://img-blog.csdnimg.cn/img_convert/968c306d4b110a5ce8ec5ac8bad80edc.png)

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

ing heroName;

    /\*\*
 \* 皮肤名
 \*/
    private String name;

    /\*\*
 \* 主图
 \*/
    private String mainImg;

    /\*\*
 \* 图标
 \*/
    private String iconImg;

    /\*\*
 \* 炫彩皮肤
 \*/
    private String chromaImg;
}


核心处理类HerosListPageProcessor

因为增加了对于英雄id的记录,以及对每次链接请求的判断(如果是列表就保存英雄数据,如果是英雄详情则下载皮肤图片)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-ysg7WyhZ-1713344980232)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;