GitHub爬虫项目详解
前言
闲来无事浏览GitHub的时候,看到一个仓库,里边列举了Java的优秀开源项目列表,包括说明、仓库地址等,还是很具有学习意义的。但是大家也知道,国内访问GitHub的时候,经常存在访问超时的问题,于是就有了这篇文章,每日自动把这些数据爬取下来,随时看到热点排行。
仓库地址:https://github.com/akullpp/awesome-java
仓库页面截图:
分析
根据以往爬虫经验,先确定好思路,再开始开发代码效率会更高。那么,第一步,找一下我们的数据来源。
具体步骤:先开启F12,刷新网页,根据关键词搜索,看数据来源是哪个接口(此处以列表里的Maven为例,其他也可以)

可以看到,项目列表都是来源于这个.md文档的1250行,可以看到,这是一个标准的JSON数据,我们把这行数据复制出来进行分析(由于数据太长,不做展示),继续搜索后发现,我们需要的项目列表和说明,都在其中richText字段里,如下:

而这个富文本数据都是Unicode编码,为了方便查看结构,我们将其转为中文,可以用如下的正则匹配,批量转换
richData = richData.replaceAll("/\\\\u([0-9a-f]{3,4})/i", "&#x\\1;");
转换完之后继续看这个富文本数据

我们需要的东西对应的是一个一个的<li>标签和<a>标签,找到数据源之后就可以正式开始开发了。
项目开发
1、准备工作
- 开发框架选择SpringBoot,持久层框架使用MyBatis。除必要的基础依赖以外,还需要引入以下依赖:
jsoup:对网页结构分析,解析数据
okhttp:HTTP客户端,访问页面使用。
fastjson:解析JSON数据 - 关系型数据库选择Mysql,非关系型数据库选择Redis
- 编辑配置文件

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;}}
获取到正文后如图所示:

接着我们使用Jsoup对网页结构进行解析,因为需要的数据处于<Script>标签,因此我们只提取这个标签数据即可,代码为:
Document document = Jsoup.parse(html);// 2.使用 getElementsByTag,拿到所有的标签 elements相当于集合类。每个element对应一个标签
Elements elements = document.getElementsByTag("script");
提取之后效果如图:

需要的数据在列表最后一位,取到之后因其是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;");
处理结果为:

转换完的字符串还是标准的HTML语法,继续用Jsoup解析结构,获取到所有的<li>标签和<a>标签

将需要的数据提取出来,再根据提取出来的数据继续爬取项目详情页,格式为: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() {@Overridepublic 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);}

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 {@Autowiredpublic 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爬虫项目详解
前言 闲来无事浏览GitHub的时候,看到一个仓库,里边列举了Java的优秀开源项目列表,包括说明、仓库地址等,还是很具有学习意义的。但是大家也知道,国内访问GitHub的时候,经常存在访问超时的问题,…...
辅助驾驶功能开发-功能对标篇(7)-NOA领航辅助系统-上汽荣威
1.横向对标参数 厂商上汽荣威车型荣威RX5(燃油车)上市时间2022Q3方案10V3R摄像头前视摄像头1*(8M)侧视摄像头4后视摄像头1环视摄像头4DMS摄像头1雷达毫米波雷达34D毫米波雷达/超声波雷达12激光雷达/域控供应商1*(宏景智驾)辅助驾驶软件供应商地平线高精度地图中海庭芯片J3合作…...
第0次 序言
突然想起有好多书没有看,或者看了也没留下任何记录,以后有空必须得好好整理才行,这次就从《Linux命令行和shell脚本编程大全开始》 本文完全是闲聊,自娱自乐,我觉得做开发是一件很快乐的事情,但是工作是开发…...
ESP32设备驱动-OLED显示单个或多个DS18B20传感器数据
OLED显示单个或多个DS18B20传感器数据 文章目录 OLED显示单个或多个DS18B20传感器数据1、DS18B20介绍2、硬件准备3、软件准备4、代码实现4.1 读取单个DS18B20数据4.2 驱动多个DS18B20传感器4.3 OLED显示DS18B20数据在本文中,我们将介绍如何ESP32驱动单个或多个DS18B20传感器,…...
MongoDB快速上手
文章目录 1、mongodb相关概念1.1、业务应用场景1.2、MongoDB简介1.3、体系结构1.3.1 数据库 (databases) 管理语法1.3.2 集合 (collection) 管理语法 1.4、数据模型1.5、MongoDB的特点 2、单机部署3、基本常用命令3.1、案例需求3.2、数据库操作3.2.1 选择和创建数据库3.2.2 数据…...
maven 初学
1. maven 安装 配置安装 路径 maven 下载位置: D:\software\apache-maven-3.8.6 默认仓库位置: C:\Users\star-dream\.m2\repository 【已更改】 本地仓库设置为:D:\software\apache-maven-3.8.6\.m2\repository 镜像已更改为阿里云中央镜像仓库 <mirrors>…...
解决WPF+Avalonia在openKylin系统下默认字体问题
一、openKylin简介 openKylin(开放麒麟) 社区是在开源、自愿、平等和协作的基础上,由基础软硬件企业、非营利性组织、社团组织、高等院校、科研机构和个人开发者共同创立的一个开源社区,致力于通过开源、开放的社区合作ÿ…...
智能合约漏洞,Dyna 事件分析
智能合约漏洞,Dyna 事件分析 1. 漏洞简介 https://twitter.com/BlockSecTeam/status/1628319536117153794 https://twitter.com/BeosinAlert/status/1628301635834486784 2. 相关地址或交易 攻击交易 1: https://bscscan.com/tx/0x7fa89d869fd1b89e…...
Elasticsearch基础篇(四):Elasticsearch7.x的官方文档学习(Set up Elasticsearch)
Set up Elasticsearch 1 Configuring Elasticsearch(配置 Elasticsearch)1.1 Setting JVM Options(设置JVM选项)1.2 Secure Settings(安全设置)Introduction(介绍)Using the Keystore(使用密钥库)Applying Changes(应用更改)Reloadable Secure Settings(可重新加载的安全设置)R…...
二叉树的遍历方式和代码
二叉树的三种遍历和代码 1.前序遍历2.中序遍历3.后序遍历4.三种遍历方式的代码实现 1.前序遍历 学习二叉树结构,最简单的方式就是遍历。所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具…...
小样本学习——匹配网络
目录 匹配网络 (1)简单介绍: (2)专业术语 (3)主要思想 (4)训练过程 问题 回答 MANN 匹配网络 (1)简单介绍: Matching netwo…...
CSS 常用样式 之字体属性
font-weight(字体粗细) 字体粗细用于设置文本的粗细程度,可以使用如下的值: normal:正常字体(默认)bold:加粗字体bolder:更加加粗lighter:更加细 代码实例…...
nodejs+vue游戏测评交流系统elementui
可以实现首页、发布招募、公司资讯、我的等,另一方面来说也可以提高在游戏测评交流方面的效率给相关管理人员的工作带来一定的便利。在我的页面可以对游戏攻略、我的收藏管理、实际上如今信息化成为一个未来的趋势或者可以说在当前现代化的城市典范中,发布招募等功能…...
1.2.OpenCV技能树--第一单元--OpenCV安装
目录 1.文章内容来源 2.OpenCV安装 3.课后习题代码复现 4.易错点总结与反思 1.文章内容来源 1.题目来源:https://edu.csdn.net/skill/opencv/opencv-662dbd65c89d4ddb9e392f44ffe16e1a?category657 2.资料来源:https://edu.csdn.net/skill/opencv/opencv-662dbd65c89d4ddb…...
全志ARM926 Melis2.0系统的开发指引⑥
全志ARM926 Melis2.0系统的开发指引⑥ 编写目的9. 系统启动流程9.1. Shell 部分9.2.Orange 和 desktop 部分9.3. app_root 加载部分9.4. home 加载部分 10. 显示相关知识概述10.1. 总体结构10.2. 显示过程10.3. 显示宽高参数关系 -. 全志相关工具和资源-.1 全志固件镜像修改工具…...
Junit单元测试为什么不能有返回值?
这个问题的产生来源于我们老师上节课说的我们班一个男生问他的想法,刚开始听到这个还觉得挺有意思,我之前使用单元测试好像下意识的就将它的返回值写为void,一般都是进行简单的测试,也从没思考过在某个单元测试中调用另一个单元测试ÿ…...
【成像光敏描记图提取和处理】成像-光电容积描记-提取-脉搏率-估计(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Ubuntu无法引导启动的修复
TLDR:使用Boot-Repair工具。 Boot-Repair Boot-Repair是一个简单的工具,用于修复您在Ubuntu中可能遇到的常见启动问题,例如在安装Windows或其他Linux发行版后无法启动Ubuntu时,或者在安装Ubuntu后无法启动Windows时,…...
Windows电脑上的多开软件是否安全?
在Windows电脑上使用多开软件可以让使用者同时运行多个相同或不同的程序,这对于某些需要同时操作多个账号或实例的用户来说非常有用。但是很多人担心使用多开软件是否安全。 多开软件的安全问题主要在于它们可能会破坏操作系统的稳定性和安全性,导致系统…...
U盘支持启动区+文件存储区的分区方法
准备新U盘 启动diskgenius ,先建立一个主分区(7G),剩余空间建立为第二分区,然后设定第二分区激活。 diskgenius格式化 用diskgenius格式化,在格式化的过程中有一个 写入dos系统的选项,在格式…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
