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

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

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

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

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

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

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

正文

  1. 将处理好变成Java对象的数据存到page对象中等待Pipeline的处理
  2. 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<HeroInfo> 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

不出所料我们找到了有用的皮肤信息

点开皮肤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的记录,以及对每次链接请求的判断(如果是列表就保存英雄数据,如果是英雄详情则下载皮肤图片)

因此改造后的核心HerosListPageProcessorprocess()函数应该如下

/\*\*
 \* 核心程序部分
 \*/
    @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<HeroInfo> heroInfoList = heros.toJavaList(HeroInfo.class);
            page.putField(Constants.HERO\_KEY, heroInfoList);
            page.putField(Constants.VERSION, version);
            page.putField(Constants.UPDATE\_TIME, updateFileTime);
            // 下载英雄图片信息,经过分析得到英雄信息详情的json信息路径(写在Constants中)
            // 需要根据每个heroId来查询对应的信息 最终返回每张图片的下载地址
            this.getImgReqList(page, heroInfoList);
        }
        // 判断当前url路径是否是英雄信息详情
        if (page.getUrl().get().contains(Constants.PIC\_URL)) {
            // 处理单只英雄详情(先获取json数据对象)
            JSONObject heroDetail = JSONObject.parseObject(page.getJson().toString());
            // 从英雄详情中获取到skins这个属性
            JSONArray skins = heroDetail.getJSONArray("skins");
            List<HeroSkin> heroSkins = skins.toJavaList(HeroSkin.class);
            // 不要炫彩皮肤,我们筛选出有主皮肤图的数据即可
            List<HeroSkin> screenSkins = heroSkins.stream().filter(item -> !item.getMainImg().isEmpty()).collect(Collectors.toList());
            // 存入页面空间中待pipeLine处理
            page.putField("skins", screenSkins);
        }

    }

    /\*\*
 \* 批量去添加所有英雄的详细信息的请求路径
 \*/
    private void getImgReqList(Page page, List<HeroInfo> heroInfoList) {
        // 根据heroId去请求不同的英雄信息
        for (HeroInfo heroInfo : heroInfoList) {
            // 拼接图片请求路径(添加目标链接)
            page.addTargetRequest(Constants.PIC\_URL+heroInfo.getHeroId()+".js");
            System.out.println(page.getJson().toString());
        }
    }

最后结果处理类LolHeroPipeline

同样的我们的最终处理类的process()函数也要添加一个额外操作

要对进来的路径进行判断,如果是英雄详情的路径则需要下载图片

// 判断路径如果是英雄详情路径则开始下载图片文件
        if (resultItems.getRequest().getUrl().contains(Constants.PIC\_URL)) {
            // 根据捕获到的信息下载图片
            List<HeroSkin> skins = resultItems.get("skins");
            // 创建文件夹存储皮肤(采用默认路径+英雄名 来作为文件夹路径)
            String saveFilePath = Constants.HERO\_PIC\_FILE + skins.get(0).getHeroName();
            FileUtil.mkdir(saveFilePath);
            for (HeroSkin skin : skins) {
                // 利用hutool工具下载文件 参数一:下载地址 参数二:保存路径
                long size = HttpUtil.downloadFile(skin.getMainImg(), FileUtil.file(saveFilePath));
                log.info("下载 {} 图片成功,大小为 {}, 存储地址为{}",skin.getName(),size,saveFilePath);
            }
        }

4.3.项目源码

完整的项目案例博主已经上传到Github中,如有需要可以直接访问下载

如果对你有参考价值的话,希望能获取你的一个start

🚪 代码仓库:lol-spider源码

🏜写在最后

本文通过实战案例的方式来讲解和应用WebMagic这个Java爬虫框架。

在爬虫的过程中,其实对于页面内容的解析是很重要的。所以在这一步的时候提醒大家需要用心一点哦。

如果本文章对你有用,请不要忘记一键三连!

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

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
img

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

,saveFilePath);
}
}


#### 4.3.项目源码



> 
> 完整的项目案例博主已经上传到`Github`中,如有需要可以直接访问下载
> 
> 
> 如果对你有参考价值的话,希望能获取你的一个`start`
> 
> 
> 


🚪 代码仓库:[lol-spider源码]( )


### 🏜写在最后



> 
> 本文通过实战案例的方式来讲解和应用`WebMagic`这个Java爬虫框架。
> 
> 
> 在爬虫的过程中,其实对于页面内容的解析是很重要的。所以在这一步的时候提醒大家需要用心一点哦。
> 
> 
> 如果本文章对你有用,请不要忘记一键三连!
> 
> 


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

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

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

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值