html解析工具类、html表格解析解析工具类

张军 1073 0
package zj.parser.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

import zj.check.util.CheckUtil;
import zj.common.KV;
import zj.common.exception.ServiceException;
import zj.java.util.JavaUtil;
import zj.parser.bean.ParamBase;
import zj.parser.factory.WebClientFactory;

/**
 * html解析工具类
 * 
 * @author SHNKCS 张军 {@link <a target=_blank href="http://www.shanghaijiadun.com">上海加盾信息科技有限公司</a>&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank href="http://www.zhangjunbk.com">张军个人网站</a>&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank href="http://user.qzone.qq.com/360901061/">张军QQ空间</a>}
 * @date 2015-11-03 21:59:00
 * @modifiyNote
 * @version 1.0
 */
public class HtmlParserUtil {
	private static Logger logger = Logger.getLogger(HtmlParserUtil.class);

	/**
	 * 获取元素集合
	 * 
	 * @param rootPage
	 *            当前页面
	 * @param xpath
	 *            路径表达式
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 * @return
	 */
	public static List<?> getHtmlElements(final HtmlPage rootPage, String xpath) {
		try {
			List<?> hes = null;
			long st = 0;
			long et = 0;
			st = System.currentTimeMillis();
			while ((hes = rootPage.getByXPath(xpath)).size() == 0) {
				et = System.currentTimeMillis();
				if (et - st > 30000) {
					return null;
				}
				Thread.sleep(10);
			}
			return hes;
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 获取元素
	 * 
	 * @param rootPage
	 *            当前页面
	 * @param xpath
	 *            路径表达式
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 * @return
	 */
	public static HtmlElement getHtmlElement(final HtmlPage rootPage, String xpath) {
		try {
			List<?> hes = getHtmlElements(rootPage, xpath);
			return (HtmlElement) hes.get(0);
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 单页面处理
	 * 
	 * @param param
	 *            参数
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public static void doNormalPaper(ParamBase param) {
		// 实例化抽取程序对象
		WebClient webClient = null;
		try {
			// 列表及详情页采集
			// 一、设置任务名称String
			// 循环提出字段值
			if (param.normalFields == null || param.normalFields.size() == 0) {
				logger.info("未设置字段");
			} else {
				// 二、设置网址String
				String homeUrl = param.homeUrl;
				// 三、设置多个字段List<Map<String,String>>
				// 1.key:字段,value:xxx
				// 2.key:字段名,value:xxx
				// 3.key:数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值:属性名)
				// 采集结果
				// 实例化抽取程序对象
				webClient = WebClientFactory.getInstance().getWebClient();
				// 获取html页面对象
				HtmlPage rootPage = null;
				// 首页列表
				rootPage = webClient.getPage(homeUrl);
				// 默认打开网页休眠1秒
				String html = rootPage.asXml();
				// System.out.println(html);
				// html内容
				Document doc = Jsoup.parse(html);
				// logger.info(rootPage.asXml());
				// 取字段值
				Map<String, Object> pageFieldValue = getPageFieldValueNormal(param, doc);
				// Thread.sleep(param.sleepCallback);
				// 回调
				param.callBack(pageFieldValue);
			}
		} catch (Exception e) {
			throw new ServiceException(e);
		} finally {
			// // 关闭资源
			// WebClientFactory.getInstance().close(webClient);
		}
	}

	/**
	 * 分页列表或表格处理
	 * 
	 * @param param
	 *            参数
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public static void doListPaper(ParamBase param) {
		// 实例化抽取程序对象
		WebClient webClient = null;
		try {
			// 正常数据
			if (param.normalFields.size() > 0) {
				// 列表及详情页采集
				// 一、设置任务名称String
				// 循环提出字段值
				doNormalPaper(param);
			}
			// 列表中的数据
			if (param.listKV.size() > 0) {
				HtmlPage rootPage = null;
				if (param.rootPage == null) {
					// 实例化抽取程序对象(由于回调只能每次都获取新页面)
					webClient = WebClientFactory.getInstance().getWebClient();
					// 二、设置网址String
					// 采集结果
					// 获取html页面对象
					// 首页列表
					rootPage = webClient.getPage(param.homeUrl);
					// 回调修改,可能进入页面要做前期查询
					rootPage = param.updateRootPage(rootPage);
				} else {
					rootPage = param.rootPage;
				}
				// 循环翻页
				for (KV<String, String> kv : param.listKV) {
					// 默认0页
					param.tempPage = 0;
					// 列表表达式
					String listXpath = kv.getK();
					// 下一页
					String nextPage = kv.getV();
					// 设置默认值
					param.tempListFields = param.listFields;
					if (param.mapListFields != null && param.mapListFields.size() > 0) {
						List<Map<String, String>> listFields = param.mapListFields.get(listXpath);
						if (listFields != null) {
							// 替换原始值
							param.tempListFields = listFields;
						}
					}
					if (param.tempListFields.size() > 0) {
						// 只处理第一页
						doListPaper(listXpath, param, rootPage);
						if (CheckUtil.isNotNull(nextPage)) {
							// 有翻页
							long st = System.currentTimeMillis();
							// 六、设置翻页Map<String,String>
							// 1.key:翻页,value:xxx
							// 2.key:翻页停止,value:xxx
							List<?> nextDoms = null;
							// 正常加载完成开始
							while (true) {
								try {
									// 条件判断开始
									nextDoms = rootPage.getByXPath(nextPage);
									// logger.info("-----" + nextPage.size());
									// 加载完成
									HtmlElement nextDom = (HtmlElement) nextDoms.get(0);
									rootPage = nextDom.click();
									// 休眠500毫秒加载页面
									Thread.sleep(param.sleepNextPageClick);
									param.checkPageReadyState(rootPage);
									// logger.info(rootPage.asXml());
									doListPaper(listXpath, param, rootPage);
									// 重置开始时间
									st = System.currentTimeMillis();
								} catch (Exception e) {
									long et = System.currentTimeMillis();
									if (et - st > 30000) {
										break;
									}
								}
							}
						} else {
							// 自定义下一页
							// 有翻页
							long st = System.currentTimeMillis();
							while (true) {
								try {
									// 条件判断开始
									rootPage = param.nextPage(rootPage);
									if (rootPage == null) {
										break;
									}
									// logger.info(rootPage.asXml());
									doListPaper(listXpath, param, rootPage);
									// 重置开始时间
									st = System.currentTimeMillis();
								} catch (Exception e) {
									long et = System.currentTimeMillis();
									if (et - st > 30000) {
										break;
									}
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			throw new ServiceException(e);
		} finally {
			// // 关闭资源
			// WebClientFactory.getInstance().close(webClient);
		}
	}

	/**
	 * 分页列表或表格处理
	 * 
	 * @param param
	 *            参数
	 * @param rootPage
	 *            当前页对象
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	private static void doListPaper(String listXPath, ParamBase param, HtmlPage rootPage) {
		try {
			// 页面+1
			param.tempPage = param.tempPage + 1;
			if (param.tempPage < 0) {
				System.out.println(param.tempPage);
				return;
			}
			// 正常处理
			// 四、设置列表String
			List<?> listBoxs = null;
			// 条件判断开始
			// 有翻页
			long st = System.currentTimeMillis();
			while ((listBoxs = rootPage.getByXPath(listXPath)).size() == 0) {
				long et = System.currentTimeMillis();
				if (et - st > 30000) {
					break;
				}
			}
			if (listBoxs == null || listBoxs.size() == 0) {
				// 加载完成
				logger.warn("根据[" + listXPath + "]未找到集合元素");
			} else {
				// 一页区域的结果
				List<Map<String, Object>> pageFieldValues = new ArrayList<>();
				for (Object listBoxObj : listBoxs) {
					DomElement listBox = (DomElement) listBoxObj;
					// logger.info(listBox.asXml());
					String xml = listBox.asXml();
					if (CheckUtil.isNotNull(param.startHtml) && CheckUtil.isNotNull(param.endHtml)) {
						// 添加头尾html
						xml = param.startHtml + xml + param.endHtml;
					}
					// 获取jsoup
					Document doc = Jsoup.parse(xml);
					// 取字段值
					Map<String, Object> pageFieldValue = getPageFieldValueList(param, doc);
					if (pageFieldValue.size() == 0) {
						// 如果未查询到数据
						continue;
					}
					// 设置页面值
					pageFieldValues.add(pageFieldValue);
				}
				// System.out.println(rootPage.getUrl() + "->" + param.homeUrl + "," + param.tempPage + "," + param.isDetail);
				// 回调
				// Thread.sleep(param.sleepCallback);
				if (param.tempIsCallBack) {
					param.callBack(listXPath, pageFieldValues);
				}
				// param.callBack(listXPath, pageFieldValues);
			}
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	/**
	 * 抽取值
	 * 
	 * @param doc
	 *            当前页对象
	 * @param pageFieldValue
	 *            抽取值
	 * @param jsoupField
	 *            字段对象
	 * @return
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	private static void extractFieldValue(Document doc, Map<String, Object> pageFieldValue, Map<String, String> jsoupField) {
		try {
			String p_name = jsoupField.get("字段名");
			String p_jqfield = jsoupField.get("字段");
			Object qfieldvalue = null;
			if (CheckUtil.isNull(p_jqfield)) {
				// 如果不写字段,取文本
				qfieldvalue = doc;
			} else {
				String p_type = jsoupField.get("数据类型");
				Elements e_fields = doc.select(p_jqfield);
				// logger.info("e_fields.size():" + e_fields.size());
				if (e_fields == null || e_fields.size() == 0) {
					logger.warn("根据[" + p_jqfield + "]未找到数据");
					return;
				}
				// 1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值:属性名
				if (CheckUtil.isNull(p_type)) {
					qfieldvalue = e_fields;
				} else {
					Element e_field = (Element) e_fields.get(0);
					if ("1".equals(p_type)) {
						qfieldvalue = e_field.text();
					} else if ("2".equals(p_type)) {
						qfieldvalue = e_field.html();
					} else if ("3".equals(p_type)) {
						qfieldvalue = e_field.outerHtml();
					} else if (p_type.startsWith("4:")) {
						String[] ptypeAry = JavaUtil.split(p_type, ":");
						if (ptypeAry.length > 1) {
							qfieldvalue = e_field.attr(ptypeAry[1]);
						} else {
							return;
						}
					} else {
						return;
					}
				}
			}
			pageFieldValue.put(p_name, qfieldvalue);
		} catch (Exception e) {
			// 不做处理
			logger.error("取field域值出错,继续下个字段取值", e);
		}
	}

	/**
	 * 获取页面值
	 * 
	 * @param param
	 *            参数
	 * @param doc
	 *            当前页对象
	 * @return
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	private static Map<String, Object> getPageFieldValueNormal(ParamBase param, Document doc) {
		// 五、设置多个字段List<Map<String,String>>
		// 取字段值
		Map<String, Object> pageFieldValue = new HashMap<>();
		// 1.key:字段,value:xxx
		// 2.key:字段名,value:xxx
		// 3.key:提取的数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值)
		for (Map<String, String> jsoupField : param.normalFields) {
			extractFieldValue(doc, pageFieldValue, jsoupField);
		}
		// 是否打开详细页
		doCallBack(false, param, pageFieldValue);
		return pageFieldValue;
	}

	/**
	 * 获取页面值
	 * 
	 * @param param
	 *            参数
	 * @param doc
	 *            当前页对象
	 * @return
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	private static Map<String, Object> getPageFieldValueList(ParamBase param, Document doc) {
		// 五、设置多个字段List<Map<String,String>>
		// 取字段值
		Map<String, Object> pageFieldValue = new HashMap<>();
		// 1.key:字段,value:xxx
		// 2.key:字段名,value:xxx
		// 3.key:提取的数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值)
		for (Map<String, String> jsoupField : param.tempListFields) {
			extractFieldValue(doc, pageFieldValue, jsoupField);
		}
		doCallBack(true, param, pageFieldValue);
		return pageFieldValue;
	}

	/**
	 * 获取页面值
	 * 
	 * @param param
	 *            参数
	 * @param pageFieldValue
	 *            值
	 * @return
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	private static void doCallBack(boolean isList, ParamBase param, Map<String, Object> pageFieldValue) {
		// 是否打开详细页
		String detailFieldName = null;
		ParamBase paramDetail = null;
		if (param.detailKV == null) {
			// 当前页码
			if (!param.isDetail) {
				// 如果非详细页
				param.pageCurrent = param.tempPage;
			}
		} else {
			detailFieldName = param.detailKV.getK();
			paramDetail = param.detailKV.getV();
			// 当前页码
			paramDetail.pageCurrent = param.tempPage;
			String detailHomeUrl = JavaUtil.objToStr(pageFieldValue.get(detailFieldName));
			if (CheckUtil.isNotNull(detailHomeUrl)) {
				// 禁止父类回调
				param.tempIsCallBack = false;
				// 是否是url
				paramDetail.homeUrl = detailHomeUrl;
				// 详细页面
				paramDetail.isDetail = true;
				// 赋值当前数据
				paramDetail.currentRangeValue = pageFieldValue;
				if (isList) {
					// 抽取详细页数据
					doListPaper(paramDetail);
				} else {
					doNormalPaper(paramDetail);
				}
			}
		}
	}
}
package zj.parser.factory;

import org.apache.log4j.Logger;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.ThreadedRefreshHandler;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebClientOptions;

/**
 * webClient工厂<br>
 * 
 * @version 1.00 (2011.12.02)
 * @author SHNKCS 张军 {@link <a href="http://user.qzone.qq.com/360901061/">张军QQ空间</a>}
 */
public class WebClientFactory {
	private transient static final Logger logger = Logger.getLogger(WebClientFactory.class);
	// 线程的本地实例存储器,用于存储WebClient实例
	private ThreadLocal<WebClient> clientThreadLocal;

	/**
	 * 构造方法,初始时线程的本地变量存储器
	 */
	private WebClientFactory() {
		clientThreadLocal = new ThreadLocal<WebClient>();
	}

	private static class SingletonHolder {
		private static final WebClientFactory INSTANCE = new WebClientFactory();
	}

	/**
	 * 获取工厂实例
	 * 
	 * @return 工厂实例
	 */
	public static final WebClientFactory getInstance() {
		return SingletonHolder.INSTANCE;
	}

	/**
	 * 获取一个模拟CHROME版本的WebClient实例
	 * 
	 * @return 模拟CHROME版本的WebClient实例
	 */
	public final WebClient getWebClient() {
		return getWebClient(BrowserVersion.CHROME);
	}

	/**
	 * 获取一个模拟CHROME版本的WebClient实例
	 * 
	 * @return 模拟browserVersion版本的WebClient实例
	 */
	// public synchronized WebClient getClient() {
	public final WebClient getWebClient(final BrowserVersion browserVersion) {
		WebClient webClient = null;
		/**
		 * 如果当前线程已有WebClient实例,则直接返回该实例 否则重新创建一个WebClient实例并存储于当前线程的本地变量存储器
		 */
		if ((webClient = clientThreadLocal.get()) == null) {
			clientThreadLocal.set(webClient = getNewWebClient(browserVersion));
		} else {
			logger.debug("线程 [ " + Thread.currentThread().getName() + " ] 已有WebClient实例,直接使用. . .");
		}
		return webClient;
	}

	/**
	 * 获取一个模拟CHROME版本的WebClient实例
	 * 
	 * @return 模拟CHROME版本的WebClient实例
	 */
	public final WebClient getNewWebClient() {
		return getNewWebClient(BrowserVersion.CHROME);
	}

	/**
	 * 获取一个模拟CHROME版本的WebClient实例
	 * 
	 * @return 模拟browserVersion版本的WebClient实例
	 */
	// public synchronized WebClient getClient() {
	public final WebClient getNewWebClient(final BrowserVersion browserVersion) {
		WebClient webClient = null;
		/**
		 * 如果当前线程已有WebClient实例,则直接返回该实例 否则重新创建一个WebClient实例并存储于当前线程的本地变量存储器
		 */
		if (browserVersion == BrowserVersion.CHROME) {
			BrowserVersion.CHROME.setBrowserLanguage("zh_CN");
		}
		webClient = new WebClient(browserVersion);
		WebClientOptions options = webClient.getOptions();
		// 设置代理
		// ProxyConfig proxyConfig = options.getProxyConfig();
		// proxyConfig.setProxyHost("116.199.115.78");
		// proxyConfig.setProxyPort(80);
		// DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider) webClient
		// .getCredentialsProvider();
		// credentialsProvider.addCredentials(proxy.getUser(), proxy.getPassword());
		webClient.setRefreshHandler(new ThreadedRefreshHandler());
		// 开启cookie管理
		webClient.getCookieManager().setCookiesEnabled(true);
		// 设置webClient的相关参数
		webClient.setAjaxController(new NicelyResynchronizingAjaxController());
		options.setJavaScriptEnabled(true);
		options.setCssEnabled(false);
		// options.setTimeout(120000);
		options.setTimeout(0);
		// 防止js语法错误抛出异常,js运行错误时,是否抛出异常
		options.setThrowExceptionOnScriptError(false);
		options.setThrowExceptionOnFailingStatusCode(false);
		options.setRedirectEnabled(true);
		// 是否使用不安全的SSL
		options.setUseInsecureSSL(true);
		// 设置javascript执行时间超时
		// webClient.setJavaScriptTimeout(300000);
		// webClient.waitForBackgroundJavaScript(300000);
		webClient.setJavaScriptTimeout(0);
		webClient.waitForBackgroundJavaScript(0);
		logger.debug("为线程 [ " + Thread.currentThread().getName() + " ] 创建新的WebClient实例!");
		return webClient;
	}

	/**
	 * 关闭内存
	 * 
	 * @param webClient
	 *            参数
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public void close() {
		close(getWebClient());
	}

	/**
	 * 关闭内存
	 * 
	 * @param webClient
	 *            参数
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public void close(WebClient webClient) {
		if (webClient == null) {
			return;
		}
		// System.out.println("关闭前:" + webClient.getCache().getSize());
		webClient.getCurrentWindow().getJobManager().removeAllJobs();
		webClient.close();
		webClient = null;
		// System.out.println("关闭后:" + webClient);
		System.gc();
	}
}
package zj.parser.bean;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import com.gargoylesoftware.htmlunit.html.HtmlPage;

import zj.common.KV;
import zj.reflect.util.MethodUtil;

/**
 * 采集参数类
 * 
 * @author SHNKCS 张军 {@link <a target=_blank href="http://www.shanghaijiadun.com">上海加盾信息科技有限公司</a>&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank href="http://www.zhangjunbk.com">张军个人网站</a>&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank href="http://user.qzone.qq.com/360901061/">张军QQ空间</a>}
 * @date 2015-11-03 21:59:00
 * @modifiyNote
 * @version 1.0
 */
public class ParamBase {
	// 日志
	private Logger logger = Logger.getLogger(this.getClass());
	/**
	 * 参数
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public Map<String, Object> params = new ConcurrentHashMap<>();
	/**
	 * 自定义首页
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public HtmlPage rootPage;
	/**
	 * 等待回调时间,默认500毫秒
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public long sleepCallback = 1000;
	/**
	 * 等待点击下一页加载页面时间,默认500毫秒
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public long sleepNextPageClick = 500;
	/**
	 * 名称
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public String name;
	/**
	 * 网址
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public String homeUrl;
	/**
	 * 列表多个字段
	 * 
	 * @see// 三、设置多个字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值:属性名)
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public List<Map<String, String>> listFields = new ArrayList<>();
	/**
	 * 普通多个字段
	 * 
	 * @see// 三、设置多个字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值:属性名)
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public List<Map<String, String>> normalFields = new ArrayList<>();

	/**
	 * 添加开始html
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public String startHtml;

	/**
	 * 添加结束html
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public String endHtml;
	/**
	 * list地址 key:listXPath,value:nextPage
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public List<KV<String, String>> listKV = new ArrayList<>();
	/**
	 * 临时字段,列表多个字段 key:listXPath,value:listFields
	 * 
	 * @see// 三、设置多个字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值:属性名)
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public Map<String, List<Map<String, String>>> mapListFields = new HashMap<>();
	/**
	 * 列表当前页(默认0页)
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public Integer pageCurrent = 0;

	// ==================================反射回调
	/**
	 * 普通抽取回调 key:类名,value:方法名
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public KV<String, String> normalClassCallback;
	/**
	 * 列表抽取回调 key:类名,value:方法名
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public KV<String, String> listClassCallback;

	/**
	 * rootPage回调修改
	 * 
	 * @param param
	 *            当前参数
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public HtmlPage updateRootPage(HtmlPage rootPage) {
		return rootPage;
	}

	/**
	 * 单页面回调地址(默认不实现)
	 * 
	 * @param pageFieldValues
	 *            一页的数据
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public void callBack(Map<String, Object> pageFieldValues) {
		if (normalClassCallback != null) {
			try {
				MethodUtil.invoke(Class.forName(normalClassCallback.getK()).newInstance(), normalClassCallback.getV(), new Object[] { this, pageFieldValues }, true);
			} catch (Exception e) {
				logger.error("单页面回调出错", e);
			}
		}
	}

	/**
	 * 列表或表格采集回调地址(默认不实现)
	 * 
	 * @param listXPath
	 *            集合表达式
	 * @param pageFieldValues
	 *            一页的数据
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public void callBack(String listXPath, List<Map<String, Object>> pageFieldValues) {
		if (listClassCallback != null) {
			try {
				MethodUtil.invoke(Class.forName(listClassCallback.getK()).newInstance(), listClassCallback.getV(), new Object[] { this, listXPath, pageFieldValues }, true);
			} catch (Exception e) {
				logger.error("列表或表格采集回调出错", e);
			}
		}
		// JJListCallBack callBack = new JJListCallBack();
		// callBack.callBack(this, listXPath, pageFieldValues);
	}

	/**
	 * 判断翻页是否完成
	 * 
	 * @param listXPath
	 *            集合表达式
	 * @param pageFieldValues
	 *            一页的数据
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public void checkPageReadyState(HtmlPage rootPage) {
		// 有翻页
		long st = System.currentTimeMillis();
		while (true) {
			if ("complete".equals(rootPage.getReadyState())) {
				break;
			} else {
				long et = System.currentTimeMillis();
				if (et - st > 10000) {
					break;
				}
			}
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 判断翻页是否完成
	 * 
	 * @param listXPath
	 *            集合表达式
	 * @param pageFieldValues
	 *            一页的数据
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public HtmlPage nextPage(HtmlPage rootPage) {
		// 有翻页
		return null;
	}

	// ==================================================
	/**
	 * 详细页面设置 key:字段名,value:详细页面对象
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public KV<String, ParamBase> detailKV;
	/**
	 * 是否是详细页
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public boolean isDetail;
	/**
	 * 当前数据对应的详细页面
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public Map<String, Object> currentRangeValue;

	/**
	 * 临时字段,当前页(默认0页)
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public Integer tempPage = 0;
	/**
	 * 临时字段,是否回调父类方法,默认回调
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public boolean tempIsCallBack = true;

	/**
	 * 临时字段,列表多个字段
	 * 
	 * @see// 三、设置多个字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:数据类型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的属性值:属性名)
	 * 
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public List<Map<String, String>> tempListFields = new ArrayList<>();

}
package zj.parser.bean;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

import zj.common.exception.ServiceException;
import zj.java.util.JavaUtil;
import zj.parser.util.HtmlTableParserUtil.IHtmlParserCall;
import zj.type.TypeUtil;

/**
 * html解析工具类接口
 * 
 * @author SHNKCS 张军 {@link <a target=_blank href="http://www.shanghaijiadun.com">上海加盾信息科技有限公司</a>&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank href="http://www.zhangjunbk.com">张军个人网站</a>&nbsp;&nbsp;&nbsp;&nbsp;<a target=_blank href="http://user.qzone.qq.com/360901061/">张军QQ空间</a>}
 * @date 2015-11-03 21:59:00
 * @modifiyNote
 * @version 1.0
 */
public class SimpleHtmlParser implements IHtmlParserCall {

	@Override
	public int pageCount(DomElement element) {
		// 直接获取文本
		return TypeUtil.Primitive.intValue(element.asText());
	}

	@Override
	public String pageUrl(String url, int page) {
		// 默认直接替换
		return MessageFormat.format(url, page);
	}

	@Override
	public DomElement titleDomElement(DomElement element) {
		// 默认当前对象
		return element;
	}

	@Override
	public String title(DomElement element) {
		// 直接获取文本
		return JavaUtil.objToStr(element.asText());
	}

	@Override
	public String titleHref(DomElement element) {
		// 直接获取href文本
		return element.getAttribute("href");
	}

	@Override
	public String content(HtmlPage page, String xpathContent) {
		try {
			// 默认的读取
			List<?> titleContents = page.getByXPath(xpathContent);
			DomElement titleContentEle = (DomElement) titleContents.get(0);
			// 获取文章内容数据
			return titleContentEle.asXml();
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	/* (non-Javadoc)
	 * @see zj.parser.util.HtmlTableParserUtil.IHtmlParserCall#getTasks()
	 */
	@Override
	public List<String> getTasks() {
		return new ArrayList<String>();
	}

	/* (non-Javadoc)
	 * @see zj.parser.util.HtmlTableParserUtil.IHtmlParserCall#callResult(java.util.Map)
	 */
	@Override
	public boolean callResult(Map<String, Object> result) {
		//默认添加至results
		return true;
	}

}
package zj.parser.bean;

import org.apache.log4j.Logger;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
import com.gargoylesoftware.htmlunit.ThreadedRefreshHandler;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebClientOptions;

/**
 * webClient工厂<br>
 * 
 * @version 1.00 (2011.12.02)
 * @author SHNKCS 张军 {@link <a href="http://user.qzone.qq.com/360901061/">张军QQ空间</a>}
 */
public class ZjWebClient {
	private transient static final Logger logger = Logger.getLogger(ZjWebClient.class);

	public ZjWebClient() {
		this(BrowserVersion.CHROME);
	}

	public ZjWebClient(final BrowserVersion browserVersion) {
		if (browserVersion == BrowserVersion.CHROME) {
			BrowserVersion.CHROME.setBrowserLanguage("zh_CN");
		}
		webClient = new WebClient(browserVersion);
		/**
		 * 如果当前线程已有WebClient实例,则直接返回该实例 否则重新创建一个WebClient实例并存储于当前线程的本地变量存储器
		 */
		WebClientOptions options = webClient.getOptions();
		// 设置代理
		// ProxyConfig proxyConfig = options.getProxyConfig();
		// proxyConfig.setProxyHost("116.199.115.78");
		// proxyConfig.setProxyPort(80);
		// DefaultCredentialsProvider credentialsProvider = (DefaultCredentialsProvider) webClient
		// .getCredentialsProvider();
		// credentialsProvider.addCredentials(proxy.getUser(), proxy.getPassword());
		webClient.setRefreshHandler(new ThreadedRefreshHandler());
		// 设置webClient的相关参数
		webClient.setAjaxController(new NicelyResynchronizingAjaxController());
		options.setJavaScriptEnabled(true);
		options.setCssEnabled(false);
		// options.setTimeout(120000);
		options.setTimeout(0);
		// 防止js语法错误抛出异常,js运行错误时,是否抛出异常
		options.setThrowExceptionOnScriptError(false);
		options.setThrowExceptionOnFailingStatusCode(false);
		options.setRedirectEnabled(true);
		// 是否使用不安全的SSL
		options.setUseInsecureSSL(true);
		// 设置javascript执行时间超时
		// webClient.setJavaScriptTimeout(300000);
		// webClient.waitForBackgroundJavaScript(300000);
		webClient.setJavaScriptTimeout(0);
		webClient.waitForBackgroundJavaScript(0);
		logger.debug("为线程 [ " + Thread.currentThread().getName() + " ] 创建新的WebClient实例!");
	}

	/**
	 * 关闭内存
	 * 
	 * @param webClient
	 *            参数
	 * @author 张军
	 * @date 2015-11-03 21:59:00
	 * @modifiyNote
	 * @version 1.0
	 */
	public void close() {
		if (webClient == null) {
			return;
		}
		// System.out.println("关闭前:" + webClient.getCache().getSize());
		webClient.getCurrentWindow().getJobManager().removeAllJobs();
		webClient.close();
		webClient = null;
		// System.out.println("关闭后:" + webClient);
		System.gc();
	}

	private WebClient webClient;

	/**
	 * 获取实例
	 * 
	 * @author 张军
	 * @date 2017年12月9日 下午3:40:11
	 * @version V1.0
	 * @return the webClient
	 */
	public WebClient getWebClient() {
		return webClient;
	}

}



更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论