前言

最近工作上遇到一个问题,后端有一个定时任务,需要用JAVA每天判断法定节假日、周末放假,上班等情况,

其实想单独通过逻辑什么的去判断中国法定节假日的放假情况,基本不可能,因为国家每一年的假期可能不一样,是人为设定的;

所以只能依靠其它手段,能想到的比较靠谱的如下:

  1. 网络接口:有些数据服务商会提供,要么是收钱的,要么是次数限制,等等各种问题,效果不理想,可控性差,我也没试过,如:https://www.juhe.cn/docs/api/id/177/aid/601或者http://apistore.baidu.com/apiworks/servicedetail/1116.html
  2. 在线解析网页信息,获取节假日情况:严重依赖被解析的网站网页,所以在选取网站的时候,要找稍微靠谱点的;
  3. 根据国家规定的法定节假日放假情况,每年录入系统,这种如果客户不怕麻烦的话。还是比较靠谱的;

本Demo将选择第二种来实现;

使用htmlunit在线解析网页信息,获取节假日情况

一开始是使用jsoup去解析网页的,效果不理想,如果网页是动态生成的时候,用jsoup遇到了各种问题,所以改成了htmlunit,总得来说htmlunit还是很强大的,能够模拟浏览器运行,被誉为java浏览器的开源实现;

首先去官网下载相关jar包,以及阅读相关文档:

http://htmlunit.sourceforge.net/

我这里解析的网页是360的万年历:

http://hao.360.cn/rili/

日历界面如下:

被解析的 HTML格式如下:

实现步骤:

1、加载页面;

2、循环等待页面加载完成(可能会有一些动态页面,是用javascript生成);

3、根据网页格式解析html内容,并提取关键信息存入封装好的对象;

注意点:

1、难点在于判断是否休假及假期类型,由于原页面并没有标明每一天的假期类型,所以这里的逻辑要自己去实现,详情参考代码;

2、之所以有个静态latestVocationName变量,是防止出现以下情况(出现该情况的概率极低;PS:方法要每天调用一次,该变量才生效):

代码实现:

定义一个中国日期类:

  1. package com.pichen.tools.getDate;
  2.  
  3. import java.util.Date;
  4.  
  5. public class ChinaDate {
  6.  
  7. /**
  8. * 公历时间
  9. */
  10. private Date solarDate;
  11.  
  12. /**
  13. * 农历日
  14. */
  15. private String lunar;
  16.  
  17. /**
  18. * 公历日
  19. */
  20. private String solar;
  21.  
  22. /**
  23. * 是否是 休
  24. */
  25. private boolean isVacation = false;
  26. /**
  27. * 如果是 休情况下的假期名字
  28. */
  29. private String VacationName = "非假期";
  30. /**
  31. * 是否是 班
  32. */
  33. private boolean isWorkFlag = false;
  34.  
  35. private boolean isSaturday = false;
  36. private boolean isSunday = false;
  37. /**
  38. * @return the solarDate
  39. */
  40. public Date getSolarDate() {
  41. return solarDate;
  42. }
  43. /**
  44. * @param solarDate the solarDate to set
  45. */
  46. public void setSolarDate(Date solarDate) {
  47. this.solarDate = solarDate;
  48. }
  49. /**
  50. * @return the lunar
  51. */
  52. public String getLunar() {
  53. return lunar;
  54. }
  55. /**
  56. * @param lunar the lunar to set
  57. */
  58. public void setLunar(String lunar) {
  59. this.lunar = lunar;
  60. }
  61. /**
  62. * @return the solar
  63. */
  64. public String getSolar() {
  65. return solar;
  66. }
  67. /**
  68. * @param solar the solar to set
  69. */
  70. public void setSolar(String solar) {
  71. this.solar = solar;
  72. }
  73.  
  74. /**
  75. * @return the isVacation
  76. */
  77. public boolean isVacation() {
  78. return isVacation;
  79. }
  80. /**
  81. * @param isVacation the isVacation to set
  82. */
  83. public void setVacation(boolean isVacation) {
  84. this.isVacation = isVacation;
  85. }
  86. /**
  87. * @return the vacationName
  88. */
  89. public String getVacationName() {
  90. return VacationName;
  91. }
  92. /**
  93. * @param vacationName the vacationName to set
  94. */
  95. public void setVacationName(String vacationName) {
  96. VacationName = vacationName;
  97. }
  98. /**
  99. * @return the isWorkFlag
  100. */
  101. public boolean isWorkFlag() {
  102. return isWorkFlag;
  103. }
  104. /**
  105. * @param isWorkFlag the isWorkFlag to set
  106. */
  107. public void setWorkFlag(boolean isWorkFlag) {
  108. this.isWorkFlag = isWorkFlag;
  109. }
  110. /**
  111. * @return the isSaturday
  112. */
  113. public boolean isSaturday() {
  114. return isSaturday;
  115. }
  116. /**
  117. * @param isSaturday the isSaturday to set
  118. */
  119. public void setSaturday(boolean isSaturday) {
  120. this.isSaturday = isSaturday;
  121. }
  122. /**
  123. * @return the isSunday
  124. */
  125. public boolean isSunday() {
  126. return isSunday;
  127. }
  128. /**
  129. * @param isSunday the isSunday to set
  130. */
  131. public void setSunday(boolean isSunday) {
  132. this.isSunday = isSunday;
  133. }
  134.  
  135. }

解析网页,并调用demo,打印本月详情,和当天详情:

  1. package com.pichen.tools.getDate;
  2. import java.io.IOException;
  3. import java.net.MalformedURLException;
  4. import java.text.DateFormat;
  5. import java.text.ParseException;
  6. import java.text.SimpleDateFormat;
  7. import java.util.ArrayList;
  8. import java.util.Date;
  9. import java.util.List;
  10.  
  11. import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
  12. import com.gargoylesoftware.htmlunit.WebClient;
  13. import com.gargoylesoftware.htmlunit.html.DomNodeList;
  14. import com.gargoylesoftware.htmlunit.html.HtmlElement;
  15. import com.gargoylesoftware.htmlunit.html.HtmlPage;
  16.  
  17. public class Main {
  18.  
  19. private static String latestVocationName="";
  20.  
  21. public String getVocationName(DomNodeList<HtmlElement> htmlElements, String date) throws ParseException{
  22. String rst = "";
  23.  
  24. boolean pastTimeFlag = false;
  25. DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
  26. Date paramDate = dateFormat.parse(date);
  27. if(new Date().getTime() >= paramDate.getTime()){
  28. pastTimeFlag = true;
  29. }
  30.  
  31. //first step //jugde if can get vocation name from html page
  32. for(int i = 0; i < htmlElements.size(); i++){
  33. HtmlElement element = htmlElements.get(i);
  34. if(element.getAttribute("class").indexOf("vacation")!=-1){
  35.  
  36. boolean hitFlag = false;
  37. String voationName = "";
  38. for(; i < htmlElements.size(); i++){
  39. HtmlElement elementTmp = htmlElements.get(i);
  40. String liDate = elementTmp.getAttribute("date");
  41.  
  42. List<HtmlElement> lunar = elementTmp.getElementsByAttribute("span", "class", "lunar");
  43. String lanarText = lunar.get(0).asText();
  44.  
  45. if(lanarText.equals("元旦")){
  46. voationName = "元旦";
  47. }else if(lanarText.equals("除夕")||lanarText.equals("春节")){
  48. voationName = "春节";
  49. }else if(lanarText.equals("清明")){
  50. voationName = "清明";
  51. }else if(lanarText.equals("国际劳动节")){
  52. voationName = "国际劳动节";
  53. }else if(lanarText.equals("端午节")){
  54. voationName = "端午节";
  55. }else if(lanarText.equals("中秋节")){
  56. voationName = "中秋节";
  57. }else if(lanarText.equals("国庆节")){
  58. voationName = "国庆节";
  59. }
  60.  
  61. if(liDate.equals(date)){
  62. hitFlag = true;
  63. }
  64.  
  65. if(elementTmp.getAttribute("class").indexOf("vacation")==-1){
  66. break;
  67. }
  68. }
  69.  
  70. if(hitFlag == true && !voationName.equals("")){
  71. rst = voationName;
  72. break;
  73. }
  74.  
  75. }else{
  76. continue;
  77. }
  78. }
  79.  
  80. //if first step fail(rarely), get from the latest Vocation name
  81. if(rst.equals("")){
  82. System.out.println("warning: fail to get vocation name from html page.");
  83.  
  84. //you can judge by some simple rule
  85.  
  86. //from the latest Vocation name
  87. rst = Main.latestVocationName;
  88. }else if(pastTimeFlag == true){
  89. //更新《当前时间,且最近一次的可见的假期名
  90. Main.latestVocationName = rst;
  91. }
  92. return rst;
  93. }
  94.  
  95. public List<ChinaDate> getCurrentDateInfo(){
  96. WebClient webClient = null;
  97. List<ChinaDate> dateList = null;
  98.  
  99. try{
  100. DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
  101. dateList = new ArrayList<ChinaDate>();
  102.  
  103. webClient = new WebClient();
  104. HtmlPage page = webClient.getPage("http://hao.360.cn/rili/");
  105.  
  106. //最大等待60秒
  107. for(int k = 0; k < 60; k++){
  108. if(!page.getElementById("M-dates").asText().equals("")) break;
  109. Thread.sleep(1000);
  110. }
  111.  
  112. //睡了8秒,等待页面加载完成...,有时候,页面可能获取不到,不稳定()
  113. //Thread.sleep(8000);
  114.  
  115. DomNodeList<HtmlElement> htmlElements = page.getElementById("M-dates").getElementsByTagName("li");
  116. //System.out.println(htmlElements.size());
  117.  
  118. for(HtmlElement element : htmlElements){
  119. ChinaDate chinaDate = new ChinaDate();
  120.  
  121. List<HtmlElement> lunar = element.getElementsByAttribute("span", "class", "lunar");
  122. List<HtmlElement> solar = element.getElementsByAttribute("div", "class", "solar");
  123.  
  124. chinaDate.setLunar(lunar.get(0).asText());
  125. chinaDate.setSolar(solar.get(0).asText());
  126. chinaDate.setSolarDate(dateFormat.parse(element.getAttribute("date")));
  127.  
  128. if(element.getAttribute("class").indexOf("vacation")!=-1){
  129. chinaDate.setVacation(true);
  130. chinaDate.setVacationName(this.getVocationName(htmlElements, element.getAttribute("date")));
  131.  
  132. }
  133.  
  134. if(element.getAttribute("class").indexOf("weekend")!=-1 &&
  135. element.getAttribute("class").indexOf("last")==-1){
  136. chinaDate.setSaturday(true);
  137. }
  138. if(element.getAttribute("class").indexOf("last weekend")!=-1){
  139. chinaDate.setSunday(true);
  140. }
  141. if(element.getAttribute("class").indexOf("work")!=-1){
  142. chinaDate.setWorkFlag(true);
  143. }else if(chinaDate.isSaturday() == false &&
  144. chinaDate.isSunday() == false &&
  145. chinaDate.isVacation() == false ){
  146. chinaDate.setWorkFlag(true);
  147. }else{
  148. chinaDate.setWorkFlag(false);
  149. }
  150.  
  151. dateList.add(chinaDate);
  152. }
  153.  
  154. }catch(Exception e){
  155. e.printStackTrace();
  156. System.out.println("get date from http://hao.360.cn/rili/ error~");
  157. }finally{
  158. webClient.close();
  159. }
  160. return dateList;
  161. }
  162.  
  163. public ChinaDate getTodayInfo(){
  164. List<ChinaDate> dateList = this.getCurrentDateInfo();
  165. DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
  166. for(ChinaDate date: dateList){
  167. if(dateFormat.format(date.getSolarDate()).equals(dateFormat.format(new Date()))){
  168. return date;
  169. }
  170. }
  171. return new ChinaDate();
  172. }
  173.  
  174. public static void main(String[] args) throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {
  175.  
  176. List<ChinaDate> dateList = new Main().getCurrentDateInfo();
  177. ChinaDate today = new Main().getTodayInfo();
  178. DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
  179.  
  180. System.out.println("本月详情:");
  181. for(ChinaDate date: dateList){
  182. System.out.println(dateFormat.format(date.getSolarDate()) + " " + date.getVacationName());
  183. }
  184.  
  185. System.out.println("------------------------------------------------------------------------");
  186. System.out.println("今日详情:");
  187. System.out.println("日期:" + today.getSolarDate());
  188. System.out.println("农历:"+today.getLunar());
  189. System.out.println("公历:"+today.getSolar());
  190. System.out.println("假期名:"+today.getVacationName());
  191. System.out.println("是否周六:"+today.isSaturday());
  192. System.out.println("是否周日:"+today.isSunday());
  193. System.out.println("是否休假:"+today.isVacation());
  194. System.out.println("是否工作日:"+today.isWorkFlag());
  195.  
  196. System.out.println("已发生的最近一次假期:" + Main.latestVocationName);
  197. }
  198.  
  199. }

运行程序,结果正确:

后续改进措施

当网页加载失败的时候,可以多次尝试;

可以考虑多找几个网站的日历进行解析,当其中一个抛出异常的时候,切换到另一个网站解析;

考虑增加邮件通知或短信通知功能,出现任何异常信息都能实时通知系统管理者;

使用htmlunit在线解析网页信息的更多相关文章

  1. 安卓TV开发(七) 移动智能终端多媒体之在线解析网页视频源

    载请标明出处:http://blog.csdn.net/sk719887916/article/details/40049137,作者:skay 结束了所有UI绘制的学习,智能设备常用的应用音视频类, ...

  2. 使用java开源工具httpClient及jsoup抓取解析网页数据

    今天做项目的时候遇到这样一个需求,需要在网页上展示今日黄历信息,数据格式如下 公历时间:2016年04月11日 星期一 农历时间:猴年三月初五 天干地支:丙申年 壬辰月 癸亥日 宜:求子 祈福 开光 ...

  3. 一、使用 BeautifulSoup抓取网页信息信息

    一.解析网页信息 from bs4 import BeautifulSoup with open('C:/Users/michael/Desktop/Plan-for-combating-master ...

  4. httpclient+jsoup实现网页信息抓取

    需求分析:抓取:http://tools.2345.com/rili.htm中的万年历(阳历.阴历等等). 1.首先为抓取的内容创建一个类.实现封装. package com.wan.domain; ...

  5. [java] jsoup 解析网页获取省市区域信息

    到国家统计局抓取数据, 到该class下解析数据 /** * jsoup解析网页 * @author xwolf * @date 2016-12-13 18:11 * @since V1.0.0 */ ...

  6. python网络爬虫之解析网页的XPath(爬取Path职位信息)[三]

    目录 前言 XPath的使用方法 XPath爬取数据 后言 @(目录) 前言 本章同样是解析网页,不过使用的解析技术为XPath. 相对于之前的BeautifulSoup,我感觉还行,也是一个比较常用 ...

  7. python--爬虫入门(八)体验HTMLParser解析网页,网页抓取解析整合练习

    python系列均基于python3.4环境  基本概念 html.parser的核心是HTMLParser类.工作的流程是:当你feed给它一个类似HTML格式的字符串时,它会调用goahead方法 ...

  8. 网页信息抓取进阶 支持Js生成数据 Jsoup的不足之处

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/23866427 今天又遇到一个网页数据抓取的任务,给大家分享下. 说道网页信息抓取 ...

  9. HttpClient+Jsoup 抓取网页信息(网易贵金属为例)

    废话不多说直接讲讲今天要做的事. 利用HttpClient和Jsoup技术抓取网页信息.HttpClient是支持HTTP协议的客户端编程工具包,并且它支持HTTP协议. jsoup 是一款基于 Ja ...

随机推荐

  1. Django--models基础

    需求 了解models字段和参数​ 速查 models.py 1 2 3 class UserInfo(models.Model):     ctime = models.DateTimeField( ...

  2. timestamp的两个属性:CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP

    timestamp有两个属性,分别是CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP两种,使用情况分别如下: 1. CURRENT_TIMESTAMP 当要 ...

  3. 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLock

    [源码下载] 重新想象 Windows 8 Store Apps (46) - 多线程之线程同步: Lock, Monitor, Interlocked, Mutex, ReaderWriterLoc ...

  4. 基于FreeBSD 64位内核的kFreeBSD无法在Virtualbox下安装

    ArchBSD同上 感谢大A(豆瓣)的投稿 :)

  5. (旧)子数涵数·UI设计——扁平化设计

    一.基本资料 1.由来 扁平化设计这个概念,是由Google(谷歌)在2008年提出的:它的首个实践者是microsoft(微软),microsoft在2012年发行了win8系统,这个系统的外观主题 ...

  6. 关于我的OI生涯(AFO){NOIP2016 后}

    这篇我就随意写啦~不用统一的“题解”形式.♪(^∀^●)ノ 也分好几次慢慢更吧~ 对于NOIP2016的总结,我本想善始善终back回,但是心情不足以支撑我,那就只能有始有终了......下面进入我的 ...

  7. 浅谈一下缓存策略以及memcached 、redis区别

    缓存策略三要素:缓存命中率   缓存更新策略  最大缓存容量.衡量一个缓存方案的好坏标准是:缓存命中率.缓存命中率越高,缓存方法设计的越好. 三者之间的关系为:当缓存到达最大的缓存容量时,会触发缓存更 ...

  8. 使用Jsoup解析html网页

    一.   JSOUP简介 在以往用java来处理解析HTML文档或者片段时,我们通常会采用htmlparser(http://htmlparser.sourceforge.net/)这个开源类库.现在 ...

  9. 「C语言」C输出hello world!系统发生了什么?

    本篇文章全部摘抄自学长博客供以后学习: http://efraim.me/2015/12/05/tech-linux-2015-12-05/ 排版因与博客园编辑器不同而稍作修改. 输出hello wo ...

  10. 【GOF23设计模式】状态模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_状态模式.UML状态图.酒店系统房间状态.线程对象状态切换 package com.test.state; public ...