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.dlhighland.cn">張軍個人網(wǎng)站</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
* 當(dāng)前頁面
* @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
* 當(dāng)前頁面
* @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
* 參數(shù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public static void doNormalPaper(ParamBase param) {
// 實例化抽取程序?qū)ο?
WebClient webClient = null;
try {
// 列表及詳情頁采集
// 一、設(shè)置任務(wù)名稱String
// 循環(huán)提出字段值
if (param.normalFields == null || param.normalFields.size() == 0) {
logger.info("未設(shè)置字段");
} else {
// 二、設(shè)置網(wǎng)址String
String homeUrl = param.homeUrl;
// 三、設(shè)置多個字段List<Map<String,String>>
// 1.key:字段,value:xxx
// 2.key:字段名,value:xxx
// 3.key:數(shù)據(jù)類型,value:(1:抓取文本、2:抓取元素的innerHtml、3:抓取元素的outerHtml、4:抓取元素的屬性值:屬性名)
// 采集結(jié)果
// 實例化抽取程序?qū)ο?
webClient = WebClientFactory.getInstance().getWebClient();
// 獲取html頁面對象
HtmlPage rootPage = null;
// 首頁列表
rootPage = webClient.getPage(homeUrl);
// 默認打開網(wǎng)頁休眠1秒
String html = rootPage.asXml();
// System.out.println(html);
// html內(nèi)容
Document doc = Jsoup.parse(html);
// logger.info(rootPage.asXml());
// 取字段值
Map<String, Object> pageFieldValue = getPageFieldValueNormal(param, doc);
// Thread.sleep(param.sleepCallback);
// 回調(diào)
param.callBack(pageFieldValue);
}
} catch (Exception e) {
throw new ServiceException(e);
} finally {
// // 關(guān)閉資源
// WebClientFactory.getInstance().close(webClient);
}
}
/**
* 分頁列表或表格處理
*
* @param param
* 參數(shù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public static void doListPaper(ParamBase param) {
// 實例化抽取程序?qū)ο?
WebClient webClient = null;
try {
// 正常數(shù)據(jù)
if (param.normalFields.size() > 0) {
// 列表及詳情頁采集
// 一、設(shè)置任務(wù)名稱String
// 循環(huán)提出字段值
doNormalPaper(param);
}
// 列表中的數(shù)據(jù)
if (param.listKV.size() > 0) {
HtmlPage rootPage = null;
if (param.rootPage == null) {
// 實例化抽取程序?qū)ο?由于回調(diào)只能每次都獲取新頁面)
webClient = WebClientFactory.getInstance().getWebClient();
// 二、設(shè)置網(wǎng)址String
// 采集結(jié)果
// 獲取html頁面對象
// 首頁列表
rootPage = webClient.getPage(param.homeUrl);
// 回調(diào)修改,可能進入頁面要做前期查詢
rootPage = param.updateRootPage(rootPage);
} else {
rootPage = param.rootPage;
}
// 循環(huán)翻頁
for (KV<String, String> kv : param.listKV) {
// 默認0頁
param.tempPage = 0;
// 列表表達式
String listXpath = kv.getK();
// 下一頁
String nextPage = kv.getV();
// 設(shè)置默認值
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();
// 六、設(shè)置翻頁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 {
// // 關(guān)閉資源
// WebClientFactory.getInstance().close(webClient);
}
}
/**
* 分頁列表或表格處理
*
* @param param
* 參數(shù)
* @param rootPage
* 當(dāng)前頁對象
* @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;
}
// 正常處理
// 四、設(shè)置列表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("根據(jù)[" + listXPath + "]未找到集合元素");
} else {
// 一頁區(qū)域的結(jié)果
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) {
// 如果未查詢到數(shù)據(jù)
continue;
}
// 設(shè)置頁面值
pageFieldValues.add(pageFieldValue);
}
// System.out.println(rootPage.getUrl() + "->" + param.homeUrl + "," + param.tempPage + "," + param.isDetail);
// 回調(diào)
// Thread.sleep(param.sleepCallback);
if (param.tempIsCallBack) {
param.callBack(listXPath, pageFieldValues);
}
// param.callBack(listXPath, pageFieldValues);
}
} catch (Exception e) {
throw new ServiceException(e);
}
}
/**
* 抽取值
*
* @param doc
* 當(dāng)前頁對象
* @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("數(shù)據(jù)類型");
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("根據(jù)[" + p_jqfield + "]未找到數(shù)據(jù)");
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域值出錯,繼續(xù)下個字段取值", e);
}
}
/**
* 獲取頁面值
*
* @param param
* 參數(shù)
* @param doc
* 當(dāng)前頁對象
* @return
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
private static Map<String, Object> getPageFieldValueNormal(ParamBase param, Document doc) {
// 五、設(shè)置多個字段List<Map<String,String>>
// 取字段值
Map<String, Object> pageFieldValue = new HashMap<>();
// 1.key:字段,value:xxx
// 2.key:字段名,value:xxx
// 3.key:提取的數(shù)據(jù)類型,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
* 參數(shù)
* @param doc
* 當(dāng)前頁對象
* @return
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
private static Map<String, Object> getPageFieldValueList(ParamBase param, Document doc) {
// 五、設(shè)置多個字段List<Map<String,String>>
// 取字段值
Map<String, Object> pageFieldValue = new HashMap<>();
// 1.key:字段,value:xxx
// 2.key:字段名,value:xxx
// 3.key:提取的數(shù)據(jù)類型,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
* 參數(shù)
* @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) {
// 當(dāng)前頁碼
if (!param.isDetail) {
// 如果非詳細頁
param.pageCurrent = param.tempPage;
}
} else {
detailFieldName = param.detailKV.getK();
paramDetail = param.detailKV.getV();
// 當(dāng)前頁碼
paramDetail.pageCurrent = param.tempPage;
String detailHomeUrl = JavaUtil.objToStr(pageFieldValue.get(detailFieldName));
if (CheckUtil.isNotNull(detailHomeUrl)) {
// 禁止父類回調(diào)
param.tempIsCallBack = false;
// 是否是url
paramDetail.homeUrl = detailHomeUrl;
// 詳細頁面
paramDetail.isDetail = true;
// 賦值當(dāng)前數(shù)據(jù)
paramDetail.currentRangeValue = pageFieldValue;
if (isList) {
// 抽取詳細頁數(shù)據(jù)
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;
/**
* 構(gòu)造方法,初始時線程的本地變量存儲器
*/
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;
/**
* 如果當(dāng)前線程已有WebClient實例,則直接返回該實例 否則重新創(chuàng)建一個WebClient實例并存儲于當(dāng)前線程的本地變量存儲器
*/
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;
/**
* 如果當(dāng)前線程已有WebClient實例,則直接返回該實例 否則重新創(chuàng)建一個WebClient實例并存儲于當(dāng)前線程的本地變量存儲器
*/
if (browserVersion == BrowserVersion.CHROME) {
BrowserVersion.CHROME.setBrowserLanguage("zh_CN");
}
webClient = new WebClient(browserVersion);
WebClientOptions options = webClient.getOptions();
// 設(shè)置代理
// 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);
// 設(shè)置webClient的相關(guān)參數(shù)
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);
// 設(shè)置javascript執(zhí)行時間超時
// webClient.setJavaScriptTimeout(300000);
// webClient.waitForBackgroundJavaScript(300000);
webClient.setJavaScriptTimeout(0);
webClient.waitForBackgroundJavaScript(0);
logger.debug("為線程 [ " + Thread.currentThread().getName() + " ] 創(chuàng)建新的WebClient實例!");
return webClient;
}
/**
* 關(guān)閉內(nèi)存
*
* @param webClient
* 參數(shù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public void close() {
close(getWebClient());
}
/**
* 關(guān)閉內(nèi)存
*
* @param webClient
* 參數(shù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public void close(WebClient webClient) {
if (webClient == null) {
return;
}
// System.out.println("關(guān)閉前:" + webClient.getCache().getSize());
webClient.getCurrentWindow().getJobManager().removeAllJobs();
webClient.close();
webClient = null;
// System.out.println("關(guān)閉后:" + 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;
/**
* 采集參數(shù)類
*
* @author SHNKCS 張軍 {@link <a target=_blank href="http://www.shanghaijiadun.com">上海加盾信息科技有限公司</a> <a target=_blank href="http://www.dlhighland.cn">張軍個人網(wǎng)站</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());
/**
* 參數(shù)
*
* @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;
/**
* 等待回調(diào)時間,默認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;
/**
* 網(wǎng)址
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public String homeUrl;
/**
* 列表多個字段
*
* @see// 三、設(shè)置多個字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:數(shù)據(jù)類型,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// 三、設(shè)置多個字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:數(shù)據(jù)類型,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;
/**
* 添加結(jié)束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// 三、設(shè)置多個字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:數(shù)據(jù)類型,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<>();
/**
* 列表當(dāng)前頁(默認0頁)
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public Integer pageCurrent = 0;
// ==================================反射回調(diào)
/**
* 普通抽取回調(diào) key:類名,value:方法名
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public KV<String, String> normalClassCallback;
/**
* 列表抽取回調(diào) key:類名,value:方法名
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public KV<String, String> listClassCallback;
/**
* rootPage回調(diào)修改
*
* @param param
* 當(dāng)前參數(shù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public HtmlPage updateRootPage(HtmlPage rootPage) {
return rootPage;
}
/**
* 單頁面回調(diào)地址(默認不實現(xiàn))
*
* @param pageFieldValues
* 一頁的數(shù)據(jù)
* @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("單頁面回調(diào)出錯", e);
}
}
}
/**
* 列表或表格采集回調(diào)地址(默認不實現(xiàn))
*
* @param listXPath
* 集合表達式
* @param pageFieldValues
* 一頁的數(shù)據(jù)
* @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("列表或表格采集回調(diào)出錯", e);
}
}
// JJListCallBack callBack = new JJListCallBack();
// callBack.callBack(this, listXPath, pageFieldValues);
}
/**
* 判斷翻頁是否完成
*
* @param listXPath
* 集合表達式
* @param pageFieldValues
* 一頁的數(shù)據(jù)
* @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
* 一頁的數(shù)據(jù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public HtmlPage nextPage(HtmlPage rootPage) {
// 有翻頁
return null;
}
// ==================================================
/**
* 詳細頁面設(shè)置 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;
/**
* 當(dāng)前數(shù)據(jù)對應(yīng)的詳細頁面
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public Map<String, Object> currentRangeValue;
/**
* 臨時字段,當(dāng)前頁(默認0頁)
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public Integer tempPage = 0;
/**
* 臨時字段,是否回調(diào)父類方法,默認回調(diào)
*
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public boolean tempIsCallBack = true;
/**
* 臨時字段,列表多個字段
*
* @see// 三、設(shè)置多個字段List<Map<String,String>> @see// 1.key:字段,value:xxx @see// 2.key:字段名,value:xxx @see// 3.key:數(shù)據(jù)類型,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.dlhighland.cn">張軍個人網(wǎng)站</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) {
// 默認當(dāng)前對象
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);
// 獲取文章內(nèi)容數(shù)據(jù)
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);
/**
* 如果當(dāng)前線程已有WebClient實例,則直接返回該實例 否則重新創(chuàng)建一個WebClient實例并存儲于當(dāng)前線程的本地變量存儲器
*/
WebClientOptions options = webClient.getOptions();
// 設(shè)置代理
// 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());
// 設(shè)置webClient的相關(guān)參數(shù)
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);
// 設(shè)置javascript執(zhí)行時間超時
// webClient.setJavaScriptTimeout(300000);
// webClient.waitForBackgroundJavaScript(300000);
webClient.setJavaScriptTimeout(0);
webClient.waitForBackgroundJavaScript(0);
logger.debug("為線程 [ " + Thread.currentThread().getName() + " ] 創(chuàng)建新的WebClient實例!");
}
/**
* 關(guān)閉內(nèi)存
*
* @param webClient
* 參數(shù)
* @author 張軍
* @date 2015-11-03 21:59:00
* @modifiyNote
* @version 1.0
*/
public void close() {
if (webClient == null) {
return;
}
// System.out.println("關(guān)閉前:" + webClient.getCache().getSize());
webClient.getCurrentWindow().getJobManager().removeAllJobs();
webClient.close();
webClient = null;
// System.out.println("關(guān)閉后:" + 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;
}
}
本文為張軍原創(chuàng)文章,轉(zhuǎn)載無需和我聯(lián)系,但請注明來自張軍的軍軍小站,個人博客http://www.dlhighland.cn
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

