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

