前言
今年事情比较多,没花精力研究数据。最近也有很多网友找我要数据,也问我能不能爬取到历史的疫情数据,最好是从疫情刚开始以来的数据。我忙完手里事后,便开始研究如何获取每个省份的历史数据。
我在网上找了很久,发现并没有哪个网站显示有新冠疫情历史数据,只有每个省的官网上会每天公布本省的疫情数据。所以要想获取全部的历史疫情数据就只有找到每个省的官网获取每一天的公布的数据。这个工作量着实很恐怖,不过所幸我完成了一个开端,获取了贵州省的历史疫情数据,提供给需要的人,和大家一起交流。
所用工具
我之前获取数据是获取的当前疫情数据,是用python语言编写的爬虫脚本。我这次总结了一下,用脚本爬取数据虽然方便快捷但局限性很强,不可扩展,例如后期要使用这个数据时,数据和系统之间没有衔接性。所以本次我采取Java语言爬取数据,并且基于Singboot框架实现数据的获取。后期我还要基于此框架和利用获取的数据制作各种酷炫的可视化大屏。这样就实现了爬取数据和展示数据在同一个系统中进行。
数据获取代码
开始之前需要搭建对应的Springboot系统,这一步网上很多,不懂请自行百度。系统搭建好了之后便直接开始进入正题。爬虫获取数据分为请求网页、数据解析和数据存储。本次请求网页我用的是Jsoup工具,数据解析采用的是Jsoup的选择器和正则表达式共同实现,获取到的数据存入mysql数据库中。
经过对网页的分析可以发现,要想获得每一天的数据就需要在每个省的官网获取对应每一天的链接,最后在通过每一天的链接进入到对应的网页获取需要的信息。对应的爬虫代码如下:
@Service
public class UrlServiceImpl implements UrlService {
@Autowired
private UrlDao urlDao;
@Override
public int addGuiZhouUrl(){
// String URL = "http://www.gzhfpc.gov.cn/xwzx_500663/tzgg/index.html";
List<String> urlList = new LinkedList<>();
urlList.add("http://www.gzhfpc.gov.cn/xwzx_500663/tzgg/index.html");
//确保能访问第一个网址
for (int i=1;i<=117;i++){
String URL = "http://www.gzhfpc.gov.cn/xwzx_500663/tzgg/index_"+i+".html";
urlList.add(URL);
}
for (int j=0;j<urlList.size();j++){
GuiZhouUrlGet(urlList.get(j));
}
return 0;
}
public List<Url> getAllGuiZhouUrl(){
List<Url> urlList = urlDao.getAllUrl();
return urlList;
}
public int GuiZhouUrlGet(String URL){
ReExpression.GuiZhou guiZhou = new ReExpression.GuiZhou();
Url url = new Url();
try {
//用jsoup获取数据
Document document = Jsoup.connect(URL).get();
Elements links = document.getElementsByClass("right_list f_r f14");
for (Element link : links){
Elements ul = link.getElementsByTag("ul");
for (Element li : ul) {
//从这里开始用正则表达式获取
Pattern pattern_text = Pattern.compile(guiZhou.text);
Matcher matcher_text = pattern_text.matcher(li.toString());
Pattern pattern_title = Pattern.compile(guiZhou.re_title);
Pattern pattern_url = Pattern.compile(guiZhou.re_url);
//循环获取每一天的链接
while (matcher_text.find()){
Matcher matcher_title = pattern_title.matcher(matcher_text.group());
Matcher matcher_url = pattern_url.matcher(matcher_text.group());
if (matcher_title.find()){
//在这里存入数据库
String title_1 = matcher_title.group();
String title_2 = title_1.substring(1,title_1.length()-1);
url.setGuizhou_title(title_2);
System.out.println(title_2);
}
if (matcher_url.find()){
//在这里存入数据库
String url_1 = matcher_url.group();
String url_2 = url_1.substring(2,url_1.length()-1);
String url_3 = "http://www.gzhfpc.gov.cn/xwzx_500663/tzgg"+url_2;
url.setGuizhou_url(url_3);
System.out.println(url_3);
}
urlDao.addUrl(url);
}
}
}
}catch (IOException e){
System.out.println("爬取数据出现异常:"+e.getMessage());
}
return 0;
}
}
这是对应获取对应的省份每一天公布疫情数据的链接,结果如下
以下则是对应到对应省份每一天的公告上获取数据的相关代码:
@Service
public class GuiZhouServiceImpl implements GuiZhouService {
@Autowired
private GuiZhouDao guiZhouDao;
@Autowired
private UrlDao urlDao;
@Override
public int saveGuiZhouData() {
List<Url> urlList = urlDao.getAllUrl();
for (int i=1;i<urlList.size();i++){
GuiZhouDateGet(urlList.get(i).getGuizhou_url());
}
// GuiZhouDateGet("http://www.gzhfpc.gov.cn/xwzx_500663/tzgg/202110/t20211024_71186471.html");
return 0;
}
public int GuiZhouDateGet(String url){
//罗列出前端页面的全部类
String className = "view TRS_UEDITOR trs_paper_default trs_web";
String className1 = "view TRS_UEDITOR trs_paper_default trs_word trs_key4format";
String className2 = "view TRS_UEDITOR trs_paper_default trs_word";
String className3 = "view TRS_UEDITOR trs_paper_default trs_web trs_word";
String className4 = "view TRS_UEDITOR trs_paper_default trs_word trs_web";
String text = getRequest(url,className);
//根据不同的类获取数据
if (!text.isEmpty()){
set_entity(text);
}else {
String text1 = getRequest(url,className1);
if (!text1.isEmpty()){
set_entity(text1);
}else {
String text2 = getRequest(url,className2);
if (!text2.isEmpty()){
set_entity(text2);
}else {
String text3 = getRequest(url,className3);
if (!text3.isEmpty()){
set_entity(text3);
}else {
String text4 = getRequest(url,className4);
set_entity(text4);
}
}
}
}
return 0;
}
/**
* 用正则表达是解析获取文本数据中的数字信息
* @param re_text
* @param re_number
* @param text
* @return
*/
public int re_guizhou(String re_text,String re_number,String text){
Matcher matcher_text = Pattern.compile(re_text).matcher(text);
int data = 0;
if (matcher_text.find()){
String matcher_text_1 = matcher_text.group();
Matcher matcher_result = Pattern.compile(re_number).matcher(matcher_text_1);
if (matcher_result.find())
data = new Integer(matcher_result.group());
}
return data;
}
/**
* 调用re_guizhou方法将解析的数据写入实体类并写入数据库
* @param text
* @return
*/
public int set_entity(String text){
GuiZhou guiZhou_enity = new GuiZhou();
ReExpression.GuiZhou guiZhou = new ReExpression.GuiZhou();
//获取日期
Matcher matcher_time = Pattern.compile(guiZhou.time).matcher(text);
if (matcher_time.find())
guiZhou_enity.setTime(matcher_time.group());
//获取累计确诊人数
guiZhou_enity.setLeiJiqueJhen(re_guizhou(guiZhou.re_leijiquezhen,guiZhou.number,text));
//获取境外输入人数
guiZhou_enity.setJingWaiShuRu(re_guizhou(guiZhou.re_jingwaishuru,guiZhou.number,text));
//获取累计治愈人数
guiZhou_enity.setLeiJiZhiYu(re_guizhou(guiZhou.re_leijizhiyu,guiZhou.number,text));
//获取死亡人数
guiZhou_enity.setSiWang(re_guizhou(guiZhou.re_siwang,guiZhou.number,text));
//获取现有本土人数
guiZhou_enity.setXianYouQueZhen(re_guizhou(guiZhou.re_xianyoubentu,guiZhou.number,text));
//获取疑似病例人数
guiZhou_enity.setYiSiBingLi(re_guizhou(guiZhou.re_yisibingli,guiZhou.number,text));
//获取无症状人数
guiZhou_enity.setWuZhengZhuang(re_guizhou(guiZhou.re_wuzhengzhuang,guiZhou.number,text));
System.out.println(JSON.toJSONString(guiZhou_enity));
guiZhouDao.saveGuiZhouData(guiZhou_enity);
return 0;
}
/**
* 发送获取数据请求并用类选择器返回数据文本
* @param url
* @param className
* @return
*/
public String getRequest(String url,String className){
try{
Document document = Jsoup.connect(url).get();
Elements elements = document.getElementsByClass(className);
return elements.toString();
}catch (IOException e){
System.out.println(e.toString());
return "";
}
}
}
以上是对应服务层的部分代码,仅供参考和学习交流使用单独是无法运行的,代码中对应的注释都有。
结果展示
以下是存入mysql中的结果数据:
以下是导出到Excel表格中时的数据:
结语
本次获取数据我只是以贵州省为例,后期还会更新其他的历史疫情数据,并且还会利用相应的数据制作对应的可视化动态图表。
经过元旦期间的努力,所有的新冠肺炎疫情数据我已爬取完成,具体的见我的下一篇博客:历史数据
备注:本次疫情数据来自贵州省卫生健康委员会官方网站