java htmlUnit爬虫日历
2022-03-09 本文已影响0人
wsj1211
爬取www.wannianli.tianqi.com
// 模仿点击类
public HtmlPage getDateHtml(HtmlPage page, String btn) throws IOException {
HtmlSelect select = (HtmlSelect) page.getElementById("month_select");
HtmlPage hp = select.click();
HtmlAnchor anchor = (HtmlAnchor) page.getByXPath(btn).get(0);
anchor.click();
return hp;
}
// webClient 工具类
package org.rcisoft.internal.core.util.webClient;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.WebClient;
import org.springframework.stereotype.Component;
import java.security.GeneralSecurityException;
@Component
public class ThreadLocalClientFactory {
//单例工厂模式
private final static ThreadLocalClientFactory instance =new ThreadLocalClientFactory();
public static ThreadLocalClientFactory getInstance(){
return instance;
}
// 覆写ThreadLocal的initialValue方法
//线程的本地实例存储器,用于存储WebClient实例
private ThreadLocal<WebClient> client = new ThreadLocal<WebClient>() {
@Override
//该方法ThreadLocal变量第一次get的时候执行,如果该线程已经执行过set方法,initialValue不会执行
protected synchronized WebClient initialValue(){
WebClient webClient = new WebClient(BrowserVersion.FIREFOX_3);
//设置webClient的相关参数
webClient.getCookieManager().setCookiesEnabled(true);// 开启cookie管理
webClient.setJavaScriptEnabled(true);// 开启js解析
webClient.setCssEnabled(false);
webClient.setThrowExceptionOnFailingStatusCode(true);
// webClient.setThrowExceptionOnScriptError(true);
// 当出现Http error时,程序不抛异常继续执行
webClient.setThrowExceptionOnFailingStatusCode(false);
// 防止js语法错误抛出异常
webClient.setThrowExceptionOnScriptError(false); // js运行错误时,是否抛出异常
try {
webClient.setUseInsecureSSL(true);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
// 默认是false, 设置为true的话不让你的浏览行为被记录
// webClient.setDoNotTrackEnabled(false);
// 设置Ajax异步处理控制器即启用Ajax支持
webClient.setAjaxController(new NicelyResynchronizingAjaxController());
return webClient;
}
};
public void setWebClient(WebClient wc) {
client.set(wc);
}
public WebClient getWebClient() {
return client.get();
}
}
// 主方法
public Map<String, List<InHoliday>> getMonthHtml() throws Exception {
long startTime = System.currentTimeMillis(); //获取开始时间
// 获取指定网页实体
WebClient webClient = null;
List<InHoliday> holidays = new ArrayList<>();
try {
webClient = localClientFactory.getWebClient();
HtmlPage page = getHtmlPage(urlStr, webClient);
HtmlSelect htmlSelect = (HtmlSelect) page.getElementById("month_select");
int thisMonth = Integer.valueOf(htmlSelect.getDefaultValue()) + 1;
int t = htmlSelect.getOptionSize();
for (int i = thisMonth; i < htmlSelect.getOptionSize(); i++) {
page = getDateHtml(page, "//*[@id=\"next_buttons\"]");
holidays.addAll(getHoliday(page));
}
page = getHtmlPage(urlStr, webClient);
holidays.addAll(getHoliday(page));
for (int i = thisMonth - 1; i > 0; i--) {
page = getDateHtml(page, "//*[@id=\"prev_buttons\"]");
holidays.addAll(getHoliday(page));
}
}catch (Exception e){
throw new Exception(e.getMessage());
}finally {
webClient.closeAllWindows();
}
}
// 返回节假日数据
public List<InHoliday> getHoliday(HtmlPage page) throws IOException, ParseException {
boolean isMultiHoliday = false;
String otherHolidayName = "";
String oneHolidayName = "";
// 获取年月
HtmlSelect htmlSelectM = (HtmlSelect) page.getElementById("month_select");
HtmlSelect htmlSelectY = (HtmlSelect) page.getElementById("year_select");
int thisMonth = Integer.valueOf(htmlSelectM.getDefaultValue()) + 1;
int thisYear = Integer.valueOf(htmlSelectY.getDefaultValue());
// 定义返回集合
List<InHoliday> holidays = new ArrayList<>();
//获取日历表格tbody
DomElement anchor = page.getElementsByTagName("tbody").get(0);
//获取 行
DomNodeList<HtmlElement> anchor1 = anchor.getElementsByTagName("tr");
// 遍历行
for (HtmlElement htmlElement : anchor1) {
// 获取列
DomNodeList<HtmlElement> anchor2 = htmlElement.getElementsByTagName("td");
// 遍历列
for (HtmlElement element : anchor2) {
if (element.asXml().contains("before")){
continue;
};
if (element.asXml().contains("after")){
break;
}
HtmlElement domElement = element.getElementsByTagName("div").get(0);
List<String> dateDetail = Arrays.asList(domElement.asText().split("\r\n"));
if ("休".equals(dateDetail.get(0)) || "班".equals(dateDetail.get(0))) {
InHoliday inHoliday = new InHoliday();
String holidayDate = thisYear + "-" + thisMonth + "-" + dateDetail.get(1);
inHoliday.setHolidayDate(DateUtil.parse(holidayDate));
inHoliday.setHolidayYear(String.valueOf(thisYear));
inHoliday.setIsHoliday(1);
inHoliday.setMultipleSalary("1");
if ("休".equals(dateDetail.get(0))) {
inHoliday.setWorkType(WorkTypeEnums.REST.getCode());
}else if ("班".equals(dateDetail.get(0))){
inHoliday.setWorkType(WorkTypeEnums.WORK.getCode());
}
if (null!= dateDetail.get(2) && dateDetail.get(2).contains("节")){
inHoliday.setWorkType(WorkTypeEnums.HOLIDAY.getCode());
oneHolidayName = dateDetail.get(2);
inHoliday.setHolidayName(dateDetail.get(2));
}else {
// 查询今日是否是农历节日
// 将日期转成农历
SimpleDateFormat chineseDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Calendar date = Calendar.getInstance();
date.setTime(chineseDateFormat.parse(holidayDate));
String lunarTime = new LunarUtil(date).toString();
String lunar = lunarTime.split("-")[1].length() <=1 ?
lunarTime.split("-")[0] + "0"+ lunarTime.split("-")[1]: lunarTime.replaceAll("-","");
for (String o : lFtv) {
if (o.contains(lunar)) {
// 本月有多个节日 给本月上个节日附属休息调休等赋节日名称值
if (StringUtils.isNotBlank(oneHolidayName)) {
for (InHoliday holiday : holidays) {
if (StringUtils.isBlank(holiday.getHolidayName()) &&
DateUtil.between(holiday.getHolidayDate(), inHoliday.getHolidayDate(), DateUnit.DAY) > 7) {
holiday.setHolidayName(oneHolidayName);
}
}
}
oneHolidayName = o.split("/")[1];
inHoliday.setHolidayName(o.split("/")[1]);
inHoliday.setIsHoliday(0);
break;
}
}
// 查询今日是否是阳历节日
String sLunar = (thisMonth) + (dateDetail.get(1).length() <= 1 ? "0" + dateDetail.get(1) : dateDetail.get(1));
for (String o : sFtv) {
if (o.contains(sLunar)) {
if (StringUtils.isBlank(inHoliday.getHolidayName())) {
oneHolidayName = o.split("/")[1];
inHoliday.setHolidayName(o.split("/")[1]);
inHoliday.setIsHoliday(0);
} else {
isMultiHoliday = true;
otherHolidayName = o.split("/")[1];
}
break;
}
}
}
holidays.add(inHoliday);
}
}
}
// 判断是否有重复节日
List<InHoliday> otherHolidays = new ArrayList<>();
for (int i = 0;i< holidays.size();i++) {
if (StringUtils.isBlank(holidays.get(i).getHolidayName())
&& (i -1 >=0 && DateUtil.between(holidays.get(i).getHolidayDate(),holidays.get(i-1).getHolidayDate(),DateUnit.DAY) <7) ) {
holidays.get(i).setHolidayName(oneHolidayName);
}
if (isMultiHoliday) {
InHoliday inHoliday = new InHoliday();
BeanUtil.copyProperties(holidays.get(i), inHoliday);
inHoliday.setHolidayName(otherHolidayName);
otherHolidays.add(inHoliday);
}
}
holidays.addAll(otherHolidays);
return holidays;
}
//农历节日
static List<String> lFtv = Arrays.asList(
"0101/春节",
"0115/元宵节",
"0505/端午节",
"0815/中秋节"
);
//阳历节日
static List<String> sFtv = Arrays.asList(
"0101/元旦",
"0501/劳动节",
"1001/国庆节"
);
webClient 常用方法
int getHistoryPageCacheLimit() //返回在历史中缓存的最大页数。
int getHistorySizeLimit() //返回最大页数保持在历史记录。
String getHomePage() //返回客户端当前的主页。
String getHomePage() //返回客户端当前的主页。
String getHomePage() //返回客户端当前的主页。
String getHomePage() //返回客户端当前的主页。
int getMaxInMemory() //返回内存中的最大字节,然后将内容保存到文件中
ProxyConfig getProxyConfig() //返回此客户机的代理配置
int getScreenHeight() //返回屏幕高度。
int getScreenWidth() //返回屏幕宽度。
char[] getSSLClientCertificatePassword() // 得到sslclientcertificatepassword
String[] getSSLClientCipherSuites() //获取在SSL连接上启用的密码套件。
String[] getSSLClientProtocols() //获取在SSL连接上启用的协议版本
KeyStore getSSLClientCertificateStore() //得到sslclientcertificatestore。
String getSSLInsecureProtocol() //得到了SSL协议,可用于只有当setuseinsecuressl(布尔)设置为true。
KeyStore getSSLTrustStore() //获取SSL信任库。
int getTimeout() //获取该程序的超时值
int getWebSocketMaxBinaryMessageBufferSize()
int getWebSocketMaxBinaryMessageSize()
int getWebSocketMaxTextMessageBufferSize()
int getWebSocketMaxTextMessageSize()
boolean isActiveXNative() //返回是否允许本地ActiveX组件
boolean isAppletEnabled() //如果启用了applet,则返回true
boolean isCssEnabled() //如果CSS启用,则返回true。
boolean isDoNotTrackEnabled() //如果“不跟踪”启用,则返回true。
boolean isDownloadImages() //返回是否自动下载默认的图像,或不。
boolean isGeolocationEnabled() //如果定位是使返回true。
boolean isJavaScriptEnabled() //如果启用了JavaScript并成功地加载脚本引擎,则返回true。
boolean isPopupBlockerEnabled() //如果启用了弹出窗口拦截器,则返回true
boolean isPrintContentOnFailingStatusCode() //如果结果文档的内容在失败的响应代码中被打印到控制台,则返回true。
boolean isRedirectEnabled() //返回是否重定向之后将自动从服务器收到一个重定向状态代码。
boolean isThrowExceptionOnFailingStatusCode() //如果在响应码失败时抛出异常,则返回true
boolean isThrowExceptionOnScriptError() //指示当脚本执行失败(默认)或是否应捕获并仅记录以允许页执行继续时,是否应该抛出异常。
boolean isUseInsecureSSL() //指示是否应该使用不安全的SSL。
void setAppletEnabled(boolean enabled) //启用/禁用applet支持。
void setCssEnabled(boolean enabled) //启用/禁用CSS支持。
void setDoNotTrackEnabled(boolean enabled) //启用/禁用“不跟踪”支持
void setDownloadImages(boolean downloadImages) //设置是否自动下载默认图像,或不。
void setGeolocationEnabled(boolean enabled) //启用/禁用地理定位支持
void setHistoryPageCacheLimit(int historyPageCacheLimit) //设置历史中缓存的最大页数
void setHistorySizeLimit(int historySizeLimit) //设置历史大小限制
void setHomePage(String homePage) //设置客户端主页。
void setJavaScriptEnabled(boolean enabled) //启用/禁用JavaScript支持。
void setLocalAddress(InetAddress localAddress) //设置用于请求执行的本地地址
void setMaxInMemory(int maxInMemory) //设置内存中的最大字节,然后将内容保存到文件中。
void setPopupBlockerEnabled(boolean enabled) //启用/禁用弹出窗口拦截器
void setPrintContentOnFailingStatusCode(boolean enabled) //指定在失败的响应代码的情况下,结果文档的内容是否会被打印到控制台中
void setProxyConfig(ProxyConfig proxyConfig) //集代理配置为在这个客户端
void setRedirectEnabled(boolean enabled) //是否或不设置自动重定向将在随后的一redirect券代码从服务器的状态。
void setScreenHeight(int screenHeight) //设置屏幕的高度。
void setScreenWidth(int screenWidth) //设置屏幕宽度。
void setSSLClientCertificate(InputStream certificateInputStream, String certificatePassword, String certificateType) //SSL客户端证书的使用。
void setSSLClientCipherSuites(String[] sslClientCipherSuites) //设置启用SSL连接的密码套件,NULL使用默认的套件。
void setSSLClientProtocols(String[] sslClientProtocols) //设置启用SSL连接的协议版本,NULL使用默认版本。
void setSSLInsecureProtocol(String sslInsecureProtocol) //设置SSL协议,只有当setuseinsecuressl(布尔)设置为true
void setSSLTrustStore(URL sslTrustStoreUrl, String sslTrustStorePassword, String sslTrustStoreType) //设置SSL服务器证书信任存储区。
void setThrowExceptionOnFailingStatusCode(boolean enabled) //指定是否在出现故障状态代码时抛出异常
void setThrowExceptionOnScriptError(boolean enabled) //改变该WebClient行为脚本时出现错误
void setTimeout(int timeout) //设置该程序的超时。
void setUseInsecureSSL(boolean useInsecureSSL) //如果设置为true,客户机将接受与任何主机的连接,而不管它们是否有有效证书
常用方法
HtmlPage page = webClient.getPage(url); // 抓取页面
webClient.close(); //关闭模拟的窗口
void addCookie(String cookieString, URL pageUrl, Object origin); //解析给定的cookie并将其添加到我们的cookie存储库。
AjaxController getAjaxController(); //获取当前Ajax控制器。
BrowserVersion getBrowserVersion(); //返回当前浏览器版本。
Cache getCache() //获取当前正在使用的缓存。
WebWindow getCurrentWindow() //返回此客户端的“当前”窗口。
HTMLParserListener getHTMLParserListener() //获取由html解析器生成的消息的配置侦听器。
WebWindow getCurrentWindow() //返回此客户端的“当前”窗口