GitHub爬虫项目详解

这篇具有很好参考价值的文章主要介绍了GitHub爬虫项目详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

闲来无事浏览GitHub的时候,看到一个仓库,里边列举了Java的优秀开源项目列表,包括说明、仓库地址等,还是很具有学习意义的。但是大家也知道,国内访问GitHub的时候,经常存在访问超时的问题,于是就有了这篇文章,每日自动把这些数据爬取下来,随时看到热点排行。
仓库地址:https://github.com/akullpp/awesome-java
仓库页面截图:github 爬虫,java,github,爬虫,spring,java,后端

分析

根据以往爬虫经验,先确定好思路,再开始开发代码效率会更高。那么,第一步,找一下我们的数据来源。
具体步骤:先开启F12,刷新网页,根据关键词搜索,看数据来源是哪个接口(此处以列表里的Maven为例,其他也可以)
github 爬虫,java,github,爬虫,spring,java,后端
可以看到,项目列表都是来源于这个.md文档的1250行,可以看到,这是一个标准的JSON数据,我们把这行数据复制出来进行分析(由于数据太长,不做展示),继续搜索后发现,我们需要的项目列表和说明,都在其中richText字段里,如下:
github 爬虫,java,github,爬虫,spring,java,后端
而这个富文本数据都是Unicode编码,为了方便查看结构,我们将其转为中文,可以用如下的正则匹配,批量转换

        richData = richData.replaceAll("/\\\\u([0-9a-f]{3,4})/i", "&#x\\1;");

转换完之后继续看这个富文本数据

github 爬虫,java,github,爬虫,spring,java,后端
我们需要的东西对应的是一个一个的<li>标签和<a>标签,找到数据源之后就可以正式开始开发了。

项目开发

1、准备工作

  • 开发框架选择SpringBoot,持久层框架使用MyBatis。除必要的基础依赖以外,还需要引入以下依赖:
    jsoup:对网页结构分析,解析数据
    okhttp:HTTP客户端,访问页面使用。
    fastjson:解析JSON数据
  • 关系型数据库选择Mysql,非关系型数据库选择Redis
  • 编辑配置文件
    github 爬虫,java,github,爬虫,spring,java,后端

2、项目列表解析代码开发

根据前期分析的思路,首先使用okhttp客户端,访问https://github.com/akullpp/awesome-java/blob/master/README.md页面,获取到响应正文。

    public String getPage(String url) {
        try {
            // 1.创建okhttp客户端对象
            OkHttpClient okHttpClient = new OkHttpClient();
            // 2.创建request对象 (用Request的静态类创建)
            Request request = new Request.Builder().url(url).build();
            // 3.创建一个Call对象,负责进行一次网络访问操作
            Call call = okHttpClient.newCall(request);
            // 4.发送请求到服务器,获取到response对象
            Response response = call.execute();
            // 5.判断响应是否成功
            if (!response.isSuccessful()) {
                System.out.println("请求失败!");
                return null;
            }
            return response.body().string();
        }catch (Exception e){
            log.error("请求页面出错:{}",e.getMessage());
            return null;
        }

    }

获取到正文后如图所示:
github 爬虫,java,github,爬虫,spring,java,后端

接着我们使用Jsoup对网页结构进行解析,因为需要的数据处于<Script>标签,因此我们只提取这个标签数据即可,代码为:

Document document = Jsoup.parse(html);
        // 2.使用 getElementsByTag,拿到所有的标签    elements相当于集合类。每个element对应一个标签
Elements elements = document.getElementsByTag("script");

提取之后效果如图:
github 爬虫,java,github,爬虫,spring,java,后端
需要的数据在列表最后一位,取到之后因其是HTML语法,我们需要将其处理转为标准JSON,然后根据第一步分析的结果,根据key提取richText所在的值,并将Unicode转为中文。

String li = elements.get(elements.size()-1).toString()
                .replace("<script type=\"application/json\" data-target=\"react-app.embeddedData\">","")
                .replace("</script>","");
JSONObject pageRes = JSONObject.parseObject(li);
 String richData = pageRes.getJSONObject("payload").getJSONObject("blob").getString("richText");
richData = richData.replaceAll("/\\\\u([0-9a-f]{3,4})/i", "&#x\\1;");

处理结果为:
github 爬虫,java,github,爬虫,spring,java,后端
转换完的字符串还是标准的HTML语法,继续用Jsoup解析结构,获取到所有的<li>标签和<a>标签

github 爬虫,java,github,爬虫,spring,java,后端
将需要的数据提取出来,再根据提取出来的数据继续爬取项目详情页,格式为:https://github.com/作者名/仓库名(因代码基本一致,此处不再赘述),获取项目对应的StartCount、forkCount、IssuesCount,转换为数据库实体对象并存储即可。

3、定时任务

编写定时任务代码,每天三点执行爬取任务,因为可能存在连接超时,因此增加五十次失败重试。执行结束后不管成功失败,微信推送执行结果

 private static String PageUrl = "https://github.com/akullpp/awesome-java/blob/master/README.md";


    //[秒] [分] [小时] [日] [月] [周]
    @Scheduled(cron = "0 0 3 * * ?")
    public void crawlerTaskFunction() throws InterruptedException {
        // 1.获取入口页面
        int count = 1;
        String html = crawlerService.getPage(PageUrl);
        if(html == null){
            //如果失败,重试五十次,间隔五秒
            for (int i = 0; i < 50; i++) {
                Thread.sleep(5000L);
                count++;
                log.error("抓取页面失败,正在第 {} 次重新尝试",i+1);
                html = crawlerService.getPage(PageUrl);
                if(html != null){
                    break;
                }
            }
            if(html == null){
                log.error("抓取页面失败,正在发送失败消息!");
                JSONObject re = new JSONObject();
                re.put("本次重试次数:", 50);
                re.put("时间:", MyUtils.nowTime());
                //微信推送执行结果消息
                System.out.println(MyUtils.sendMsgNoUrl(re,MsgToken,"今日任务执行失败,请手动调用接口重新爬取!"));
                return;
            }
        }
        // 2.解析入口页面,获取项目列表
        List<ProjectDTO> projects = crawlerService.parseProjectList(html);
        //发送成功消息
        log.info("抓取页面完成,开始解析!");
        JSONObject re = new JSONObject();
        re.put("时间:", MyUtils.nowTime());
        re.put("本次重试次数:", count);
        re.put("本次项目总数:", projects.size());
        //微信推送执行结果消息
        System.out.println(MyUtils.sendMsgNoUrl(re,MsgToken,"任务执行成功,请去查看效果!"));
        
        if (CollectionUtils.isEmpty(projects)) {
            return;
        }
        // 3.遍历项目列表,利用线程池实现多线程
        // executorService提交任务:1)submit 有返回结果  2)execute 无返回结果
        // 此处使用submit是为了得知是否全部遍历结束,方便进行存到数据库操作
        ExecutorService executorService = Executors.newFixedThreadPool(10);  //固定大小10的线程池

        List<Future<?>> taskResults = new ArrayList<>();
//        for (int i = 0; i < 10; i++) {
            for (int i = 0; i < projects.size(); i++) {
            ProjectDTO project = projects.get(i);
            Future<?> taskResult = executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("crawling " + project.getName() + ".....");
                        String repoName = getRepoName(project.getUrl());
                        String jsonString = crawlerService.getRepo(repoName);
                        // 解析项目数据
                        parseRepoInfo(jsonString, project);
                        System.out.println("crawling " + project.getName() + "done !");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            taskResults.add(taskResult);
        }

        // 等待所有任务执行结束,再进行下一步
        for (Future<?> taskResult : taskResults) {
            try {
                // 调用get会阻塞,直到该任务执行完毕,才会返回
                if (taskResult != null) taskResult.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        //代码到这里,说明所有任务都执行结束,结束线程池
        executorService.shutdown();
        // 4.保存到数据库
        crawlerService.batchSave(projects);
    }

github 爬虫,java,github,爬虫,spring,java,后端

4、前端调用接口开发

对前端开放两个接口,一个为数据库数据的日期列表接口,一个根据日期查询当日数据接口,同时对参数进行非空验证

    @GetMapping("/list")
    public JSONObject verifySign(@RequestParam("time") String time) {
        JSONObject resp = new JSONObject();
        if(StringUtils.isEmpty(time) || time.equals("null")){
            resp.put("code",400);
            resp.put("data",null);
            resp.put("msg","time 参数错误!");
            return resp;
        }
        resp.put("code",200);
        resp.put("msg","请求成功");
        resp.put("data",crawlerService.getListByTime(time));
        return resp;
    }

    @GetMapping("/timeList")
    public JSONObject timeList() {
        JSONObject resp = new JSONObject();
        resp.put("code",200);
        resp.put("msg","请求成功");
        resp.put("data",crawlerService.timeList());
        return resp;
    }

在根据日期查询当日数据的接口中,因其每日的数据都是固定的,因此添加redis缓存,提高性能

        String redisKey = "crawler_"+time;
        boolean containsKey = redisUtils.containThisKey(redisKey);
        if(containsKey){
            String value = redisUtils.get(redisKey);
            return JSONObject.parseArray(value,ProjectDTO.class);
        }
        List<ProjectDTO> list = crawlerMapper.getListByTime(time);
        redisUtils.set(redisKey,JSONObject.toJSONString(list));
        return list;

其中redisUtils为自己写的Redis工具类,具体代码如下:

package com.simon.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.concurrent.TimeUnit;



@Component
public class RedisUtils {

    @Autowired
    public StringRedisTemplate redisTemplate;

    public String get(String key){
        if(StringUtils.isEmpty(key)){
            return null;
        }
        return redisTemplate.opsForValue().get(key);
    }

    public boolean set(String key,String value){
        if(StringUtils.isEmpty(key) || StringUtils.isEmpty(value)){
            return false;
        }
        redisTemplate.opsForValue().set(key,value);
        return true;
    }

    public boolean setTimeOut(String key,String value,Long timeOut){
        if(StringUtils.isEmpty(key) || StringUtils.isEmpty(value)){
            return false;
        }
        redisTemplate.opsForValue().set(key,value,timeOut, TimeUnit.SECONDS);
        return true;
    }

    public boolean delete(String key){
        if(StringUtils.isEmpty(key) ){
            return false;
        }
        Boolean isDelete = redisTemplate.delete(key);
        return isDelete != null ? isDelete : false;
    }

    public boolean containThisKey(String key){
        if(StringUtils.isEmpty(key) ){
            return false;
        }
       Boolean hasKey = redisTemplate.hasKey(key);
        return hasKey != null && hasKey;
    }

}

因作者对前端不太熟练,只是实现了一些简单的数据处理逻辑,前端效果展示:github 爬虫,java,github,爬虫,spring,java,后端文章来源地址https://www.toymoban.com/news/detail-734018.html

到了这里,关于GitHub爬虫项目详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Github点赞接近 100k 的Spring Boot学习教程+实战项目推荐

    很明显的一个现象,除了一些老项目,现在 Java 后端项目基本都是基于 Spring Boot 进行开发,毕竟它这么好用以及天然微服务友好。不夸张的说, Spring Boot 是 Java 后端领域最最最重要的技术之一,熟练掌握它对于 Java 程序员至关重要。 这篇文章我会推荐一些优质的  Spring Bo

    2024年02月03日
    浏览(47)
  • Github标星98k,企业级Spring Boot项目开发实战笔记,太强了

    准备又快到了明年金三银四招聘高峰期的时间,虽然这两年因为疫情以及经济环境不太好,互联网行业的各大厂都在裁员,但是今年下半年Java行情在慢慢的复苏,在慢慢的变好。 随着上半年病态卷的常态在慢慢的退去,互联网公司在经过了一轮的裁员筛选筛选调整后,下半

    2024年02月05日
    浏览(41)
  • github上有哪些不错的Java项目?

    前言 找了一些沙雕的有趣的项目(后面也有正经的),希望能对你的学习提供些许乐趣~ 1.吃豆人 一款经典的游戏开发案例,包括地图绘制、玩家控制、NPC根据玩家坐标实时自动寻径、吃豆积分系统、能量豆功能、多关卡(共12关)、特殊物品记分功能的实现,基于Javascript开发

    2024年02月08日
    浏览(25)
  • 精心整理了优秀的GitHub开源项目,包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等,空闲的时候方便看看提高自己的视野

    精心整理了优秀的GitHub开源项目,包含前端、后端、AI人工智能、游戏、黑客工具、网络工具、AI医疗等等,空闲的时候方便看看提高自己的视野。 刚开源就变成新星的 igl,不仅获得了 2k+ star,也能提高你开发游戏的效率,摆平一切和图形有关的问题。如果这个没有那么惊艳

    2024年02月10日
    浏览(34)
  • Github上最热门的十大Java开源项目,springboot面试

    https://github.com/zhangdaiscott/jeecg-boot Star 7861 一款基于代码生成器的Java快速开发平台,开源界“小普元”超越传统商业企业级开发平台!采用前后端分离架构:SpringBoot 2.x,Ant DesignVue,Mybatis-plus,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码!号称可以帮

    2024年04月17日
    浏览(22)
  • Git的使用--如何将本地项目上传到Github(三种简单、方便的方法)(二)(详解)

    一、第一种方法: 1.首先你需要一个github账号,所以还没有的话先去注册吧! https://github.com/ 我们使用git需要先安装git工具,这里给出下载地址,下载后一路(傻瓜式安装)直接安装即可: https://git-for-windows.github.io/ 2.登陆后,进入Github首页,点击New repository新建一个项目  

    2024年02月12日
    浏览(61)
  • 逛了五年GitHub,终于整理出七大java开源技术项目文档,赶紧收藏

    前言 大家都知道 Github 是一个程序员福地,这里有各种厉害的开源框架、软件或者教程。这些东西对于我们学习和进步有着莫大的进步,所以将 Github 上非常棒的七大Java开源项目技术文档整理下来供大家学习! 深入理解Spring Cloud与微服务构建 由浅入深,全面讲解Spring Cloud基

    2024年03月08日
    浏览(23)
  • 我把Github上最牛b的Java教程和实战项目整合成了一个PDF文档

    写在前面 大家都知道 Github 是一个程序员福地,这里有各种厉害的开源框架、软件或者教程。这些东西对于我们学习和进步有着莫大的进步,所以我有了这个将 Github 上非常棒的 Java 开源项目整理下来的想法。觉得不错的话,欢迎小伙伴们去star一波。 很多小伙伴都不知道学习

    2024年02月04日
    浏览(35)
  • GitHub上13个高赞Java项目推荐,会一个就能跟面试官谈笑风生

    我见过不少程序员面试的时候刚开始谈的好好的,结果面试官一问到写过哪些项目,瞬间就哑火了。 应届生还好,面试官还会更注重你的基础方面,如果是一直CRUD的老码农跳槽被问到项目时没有拿得出手的干货,简直分分钟被pass掉,所以今天这篇文章给大家整理了GitHub上

    2024年02月16日
    浏览(30)
  • Netty是一个由JBOSS提供的开源Java框架,目前已经成为Github上的独立项目

    Netty是一个由JBOSS提供的开源Java框架,目前已经成为Github上的独立项目。它提供了一个异步、事件驱动的网络应用程序框架和工具,用于快速开发高性能、高可靠性的网络服务器和客户端程序。 Netty是一个基于NIO的客户、服务器端的编程框架,使用Netty可以确保快速和简单地开

    2024年01月16日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包