前言

最近工作上遇到一个问题,后端有一个定时任务,需要用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:方法要每天调用一次,该变量才生效):

代码实现:

定义一个中国日期类:

package com.pichen.tools.getDate;

import java.util.Date;

public class ChinaDate {

    /**
* 公历时间
*/
private Date solarDate; /**
* 农历日
*/
private String lunar; /**
* 公历日
*/
private String solar; /**
* 是否是 休
*/
private boolean isVacation = false;
/**
* 如果是 休情况下的假期名字
*/
private String VacationName = "非假期";
/**
* 是否是 班
*/
private boolean isWorkFlag = false; private boolean isSaturday = false;
private boolean isSunday = false;
/**
* @return the solarDate
*/
public Date getSolarDate() {
return solarDate;
}
/**
* @param solarDate the solarDate to set
*/
public void setSolarDate(Date solarDate) {
this.solarDate = solarDate;
}
/**
* @return the lunar
*/
public String getLunar() {
return lunar;
}
/**
* @param lunar the lunar to set
*/
public void setLunar(String lunar) {
this.lunar = lunar;
}
/**
* @return the solar
*/
public String getSolar() {
return solar;
}
/**
* @param solar the solar to set
*/
public void setSolar(String solar) {
this.solar = solar;
} /**
* @return the isVacation
*/
public boolean isVacation() {
return isVacation;
}
/**
* @param isVacation the isVacation to set
*/
public void setVacation(boolean isVacation) {
this.isVacation = isVacation;
}
/**
* @return the vacationName
*/
public String getVacationName() {
return VacationName;
}
/**
* @param vacationName the vacationName to set
*/
public void setVacationName(String vacationName) {
VacationName = vacationName;
}
/**
* @return the isWorkFlag
*/
public boolean isWorkFlag() {
return isWorkFlag;
}
/**
* @param isWorkFlag the isWorkFlag to set
*/
public void setWorkFlag(boolean isWorkFlag) {
this.isWorkFlag = isWorkFlag;
}
/**
* @return the isSaturday
*/
public boolean isSaturday() {
return isSaturday;
}
/**
* @param isSaturday the isSaturday to set
*/
public void setSaturday(boolean isSaturday) {
this.isSaturday = isSaturday;
}
/**
* @return the isSunday
*/
public boolean isSunday() {
return isSunday;
}
/**
* @param isSunday the isSunday to set
*/
public void setSunday(boolean isSunday) {
this.isSunday = isSunday;
} }

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

package com.pichen.tools.getDate;
import java.io.IOException;
import java.net.MalformedURLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomNodeList;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage; public class Main { private static String latestVocationName=""; public String getVocationName(DomNodeList<HtmlElement> htmlElements, String date) throws ParseException{
String rst = ""; boolean pastTimeFlag = false;
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
Date paramDate = dateFormat.parse(date);
if(new Date().getTime() >= paramDate.getTime()){
pastTimeFlag = true;
} //first step //jugde if can get vocation name from html page
for(int i = 0; i < htmlElements.size(); i++){
HtmlElement element = htmlElements.get(i);
if(element.getAttribute("class").indexOf("vacation")!=-1){ boolean hitFlag = false;
String voationName = "";
for(; i < htmlElements.size(); i++){
HtmlElement elementTmp = htmlElements.get(i);
String liDate = elementTmp.getAttribute("date"); List<HtmlElement> lunar = elementTmp.getElementsByAttribute("span", "class", "lunar");
String lanarText = lunar.get(0).asText(); if(lanarText.equals("元旦")){
voationName = "元旦";
}else if(lanarText.equals("除夕")||lanarText.equals("春节")){
voationName = "春节";
}else if(lanarText.equals("清明")){
voationName = "清明";
}else if(lanarText.equals("国际劳动节")){
voationName = "国际劳动节";
}else if(lanarText.equals("端午节")){
voationName = "端午节";
}else if(lanarText.equals("中秋节")){
voationName = "中秋节";
}else if(lanarText.equals("国庆节")){
voationName = "国庆节";
} if(liDate.equals(date)){
hitFlag = true;
} if(elementTmp.getAttribute("class").indexOf("vacation")==-1){
break;
}
} if(hitFlag == true && !voationName.equals("")){
rst = voationName;
break;
} }else{
continue;
}
} //if first step fail(rarely), get from the latest Vocation name
if(rst.equals("")){
System.out.println("warning: fail to get vocation name from html page."); //you can judge by some simple rule //from the latest Vocation name
rst = Main.latestVocationName;
}else if(pastTimeFlag == true){
//更新《当前时间,且最近一次的可见的假期名
Main.latestVocationName = rst;
}
return rst;
} public List<ChinaDate> getCurrentDateInfo(){
WebClient webClient = null;
List<ChinaDate> dateList = null; try{
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
dateList = new ArrayList<ChinaDate>(); webClient = new WebClient();
HtmlPage page = webClient.getPage("http://hao.360.cn/rili/"); //最大等待60秒
for(int k = 0; k < 60; k++){
if(!page.getElementById("M-dates").asText().equals("")) break;
Thread.sleep(1000);
} //睡了8秒,等待页面加载完成...,有时候,页面可能获取不到,不稳定()
//Thread.sleep(8000); DomNodeList<HtmlElement> htmlElements = page.getElementById("M-dates").getElementsByTagName("li");
//System.out.println(htmlElements.size()); for(HtmlElement element : htmlElements){
ChinaDate chinaDate = new ChinaDate(); List<HtmlElement> lunar = element.getElementsByAttribute("span", "class", "lunar");
List<HtmlElement> solar = element.getElementsByAttribute("div", "class", "solar"); chinaDate.setLunar(lunar.get(0).asText());
chinaDate.setSolar(solar.get(0).asText());
chinaDate.setSolarDate(dateFormat.parse(element.getAttribute("date"))); if(element.getAttribute("class").indexOf("vacation")!=-1){
chinaDate.setVacation(true);
chinaDate.setVacationName(this.getVocationName(htmlElements, element.getAttribute("date"))); } if(element.getAttribute("class").indexOf("weekend")!=-1 &&
element.getAttribute("class").indexOf("last")==-1){
chinaDate.setSaturday(true);
}
if(element.getAttribute("class").indexOf("last weekend")!=-1){
chinaDate.setSunday(true);
}
if(element.getAttribute("class").indexOf("work")!=-1){
chinaDate.setWorkFlag(true);
}else if(chinaDate.isSaturday() == false &&
chinaDate.isSunday() == false &&
chinaDate.isVacation() == false ){
chinaDate.setWorkFlag(true);
}else{
chinaDate.setWorkFlag(false);
} dateList.add(chinaDate);
} }catch(Exception e){
e.printStackTrace();
System.out.println("get date from http://hao.360.cn/rili/ error~");
}finally{
webClient.close();
}
return dateList;
} public ChinaDate getTodayInfo(){
List<ChinaDate> dateList = this.getCurrentDateInfo();
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
for(ChinaDate date: dateList){
if(dateFormat.format(date.getSolarDate()).equals(dateFormat.format(new Date()))){
return date;
}
}
return new ChinaDate();
} public static void main(String[] args) throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException { List<ChinaDate> dateList = new Main().getCurrentDateInfo();
ChinaDate today = new Main().getTodayInfo();
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); System.out.println("本月详情:");
for(ChinaDate date: dateList){
System.out.println(dateFormat.format(date.getSolarDate()) + " " + date.getVacationName());
} System.out.println("------------------------------------------------------------------------");
System.out.println("今日详情:");
System.out.println("日期:" + today.getSolarDate());
System.out.println("农历:"+today.getLunar());
System.out.println("公历:"+today.getSolar());
System.out.println("假期名:"+today.getVacationName());
System.out.println("是否周六:"+today.isSaturday());
System.out.println("是否周日:"+today.isSunday());
System.out.println("是否休假:"+today.isVacation());
System.out.println("是否工作日:"+today.isWorkFlag()); System.out.println("已发生的最近一次假期:" + Main.latestVocationName);
} }

运行程序,结果正确:

后续改进措施

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

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

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

使用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. [转载]SharePoint 2013搜索学习笔记之搜索构架简单概述

    Sharepoint搜索引擎主要由6种组件构成,他们分别是爬网组件,内容处理组件,分析处理组件,索引组件,查询处理组件,搜索管理组件.可以将这6种组件分别部署到Sharepoint场内的多个服务器上, ...

  2. Eclipse魔法堂:任务管理器

    一.前言        Eclipse的任务管理器为我们提供一个方便的入口查看工程代办事宜,并定位到对应的代码行继续之前的工作. 二.使用示例        示例1: /** * @Descripti ...

  3. Gradle学习系列之七——依赖管理

    在本系列的上篇文章中,我们讲到了如何使用java Plugin,在本篇文章中,我们将讲到Gradle的依赖管理. 请通过以下方式下载本系列文章的Github示例代码: git clone https: ...

  4. AC自动机 - 关于Fail指针

    fail指针可以说是AC自动机里最难理解的东西,怎样更好的理解AC自动机的fail指针? 先来看一幅图: 看这幅图上的fail指针是怎么构造的. 树上的词分别是: { he , hers , his ...

  5. .Net反射机制分析和使用

    1..NET反射的概述 .NET反射是审查元数据并动态收集关于它的类型信息的能力. 应用程序结构分为应用程序域—程序集—模块—类型—成员几个层次,公共语言运行库加载器管理应用程序域.这些域在拥有相同应 ...

  6. 【EF 译文系列】韧性连接、重试(EF 版本至少为 6)

    原文链接:Connection Resiliency / Retry Logic (EF6 onwards) 一个应用程序的数据库连接,是非常容易受其它因素影响的,比如后端的异常或者不稳定的网络连接等 ...

  7. Python入门笔记(21):Python函数(4):关于函数式编程的内建函数

    一.关于函数式编程的内建函数 apply()逐渐被舍弃,这里不讨论 1.filter() #filter(func,seq) """纯Python描述filter函数&q ...

  8. (转) 关于在IE6下 无法跳转问题

    之前在项目,用到超链接,在ie下没有问题,但是到了ie6,居然发现点击事件不起作用, 真不可思议,以前都没注意到,后来网上搜了下,问题就出在这个void(0)上!现把网上的资料整理了下. <a  ...

  9. linux下socket编程

    相关结构 //下边这两个结构定义在<sys/types.h>里 //一般的地址结构,只能用于覆盖(把其他地址转换为此类型),且只能引用该地址的sa_family字段 struct sock ...

  10. 二进制打印与逆序_C语言(转)

    //二进制逆序 by MoreWindows( http://blog.csdn.net/MoreWindows ) #include <stdio.h> //二进制打印函数 templa ...