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> <a target=_blank href="http://www.zhangjunbk.com">张军个人网站</a> <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> <a target=_blank href="http://www.zhangjunbk.com">张军个人网站</a> <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> <a target=_blank href="http://www.zhangjunbk.com">张军个人网站</a> <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; } }
本文为张军原创文章,转载无需和我联系,但请注明来自张军的军军小站,个人博客http://www.zhangjunbk.com