- 浏览: 65510 次
- 性别:
- 来自: 成都
文章分类
最新评论
-
wdfyqsdy:
请问如何设置READONLY属性,设置READONLY就不能显 ...
Ext dateField扩展 可单独选择年月,年,月.可清空 -
向往前途:
楼主,你好啊,能否传给demo给我呢?谢谢!403826222 ...
java版qWikiOffice -
a734797702:
楼主你好!做得漂亮 能否传给demo给我呢?谢谢zhangjx ...
java版qWikiOffice -
jacky66666:
拖动后有白屏的现象,请问怎么解决?
Ext4.1 desktop 实现桌面图标可拖拽 -
qifan.yang:
没有源码,居然用exe4j打包
Swing+Spring-3.1.1+Mybatis-3.1.1+C3p0-0.9.1.2+Sqlite 实现Swing版网络爬虫
说明:此次优化基于htmlparser,根据标签名称或者标签属性及属性值 过滤内容,只需要设置,内容地址集及内容参数即可采集,准确率高。
用法:
在“开始HTML”处(没有修改模板,其实现在应该叫“内容区域参数”)写上你要采集的标签名称/标签属性及属性值,如 id=artibody
在“结束HTML”处(没有修改模板,其实现在应该叫“内容区域过滤参数”)写上在内容区域内你要过滤的标签名称/标签属性及属性 值,如:type=text/javascript,class=icon_fx|blkComment otherContent_01
约定:不同标签/属性以逗号(“,”)分隔,相同属性不同值,值与值之间以"|"分隔
1,标签属性/值形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN
2,标签名称形式,如:div,p,span
3,混合形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN,div,p,span
采集参数样例:
新浪国内新闻:
内容地址集: 开始HTML: class=list_009 结束HTML :空
内容: 开始HTML: id=artibody 结束HTML :type=text/javascript,class=icon_fx|blkComment otherContent_01,style=text-align: right;padding-right:10px;|margin-top:6px;|font-size: 12px ! important;|font-size:12px,id=fxwb|fxMSN|fxMSN|comment_t_show_top,style,script
代码如下:
HTML解析工具类接口:
package com.jeecms.cms.service;
import java.util.List;
import com.jeecms.cms.entity.assist.CmsAcquisition;
/**
* HTML解析工具类接口
* @author javacoo
* @since 2011-10-31
*/
public interface ParseHtmlTool {
/**
* 取得连接集合
* @param orginHtml 原始HTML
* @return 连接集合
*/
List<String> getUrlList( String orginHtml);
/**
* 取得标题集合
* @param orginHtml 原始HTML
* @return 标题集合
*/
List<String> getTitleList(String orginHtml);
/**
* 取得指定区域的HTML内容
* @return 指定区域的HTML内容
*/
String getHtml(String orginHtml);
}
HTML解析工具,HtmlParser实现类:
package com.jeecms.cms.service;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.RemarkNode;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import com.jeecms.cms.entity.assist.CmsAcquisition;
/**
* HTML解析工具,HtmlParser实现类
* @author javacoo
* @since 2011-10-31
*/
public class HtmlParserImpl implements ParseHtmlTool{
/**连接集合标志*/
private static String LINK_LIST = "linkList";
/**标题集合标志*/
private static String TITLE_LIST = "titleList";
/**单标签标志*/
private static String SINGLE_TAG = "singleTag";
/**连接正则表达式*/
private static String LINK_REGX = "<a.*href=\"(.*?)\".*>(.*?)</a>";
/**正则表达式对象*/
private Pattern pt = Pattern.compile(LINK_REGX);
/**采集参数bean*/
private ParamBean paramBean;
public HtmlParserImpl(CmsAcquisition acqu){
parseRequestParam(acqu);
}
/**
* 取得标题集合
* @param orginHtml 原始HTML
* @return 标题集合
*/
public List<String> getTitleList(String orginHtml) {
orginHtml = getHtmlByFilter(paramBean.getLinksetStartMap(), paramBean.getLinksetEndMap(),orginHtml);
if (StringUtils.isNotEmpty(orginHtml)) {
return getUrlOrTitleListByType(orginHtml,TITLE_LIST);
}
return null;
}
/**
* 取得连接集合
* @param orginHtml 原始HTML
* @return 连接集合
*/
public List<String> getUrlList(String orginHtml) {
orginHtml = getHtmlByFilter(paramBean.getLinksetStartMap(), paramBean.getLinksetEndMap(),orginHtml);
if (StringUtils.isNotEmpty(orginHtml)) {
return getUrlOrTitleListByType(orginHtml,LINK_LIST);
}
return null;
}
/**
* 取得指定区域的HTML内容
* @param orginHtml 原始HTML
* @return 指定区域的HTML内容
* @throws ParserException
*/
public String getHtml(String orginHtml) {
orginHtml = getHtmlByFilter(paramBean.getContentStartMap(), paramBean.getContentEndMap(),orginHtml);
return orginHtml;
}
/**
* 解析采集参数,并封装到ParamBean
* @param acqu 原始采集参数
* @return 采集参数封装bean
*/
private void parseRequestParam(CmsAcquisition acqu){
paramBean = new ParamBean();
if(!StringUtils.isEmpty(acqu.getLinksetStart())){
paramBean.setLinksetStartMap(populateParamMap(acqu.getLinksetStart()));
}
if(!StringUtils.isEmpty(acqu.getLinksetEnd())){
paramBean.setLinksetEndMap(populateParamMap(acqu.getLinksetEnd()));
}
if(!StringUtils.isEmpty(acqu.getContentStart())){
paramBean.setContentStartMap(populateParamMap(acqu.getContentStart()));
}
if(!StringUtils.isEmpty(acqu.getContentEnd())){
paramBean.setContentEndMap(populateParamMap(acqu.getContentEnd()));
}
}
/**
* 得到地址集
* @param html html内容
* @param type 1 :取得连接集合,2:取得标题集合
* @return 连接或者标题集合
*/
private List<String> getUrlOrTitleListByType(String html, String type) {
List<String> resultList = new ArrayList<String>();
Matcher m = pt.matcher(html);
String result = "";
int pos = 1;
if(TITLE_LIST.equals(type)){
pos = 2;
}
while (m.find()) {
result = m.group(pos);
resultList.add(result);
}
return resultList;
}
/**
* 取得指定区域的HTML内容
* @param tagMap 标签MAP
* @param removeTagMap 要过滤的标签MAP
* @param orginHtml 原始HTML
* @return 指定区域的HTML内容
* @throws ParserException
*/
private String getHtmlByFilter(Map<String, String> tagMap,
Map<String, String> removeTagMap, String orginHtml) {
try {
Parser parser = new Parser();
parser.setInputHTML(orginHtml);
// 第一步取得指定属性/标签内容
String tempKey = null;
String tempValue = null;
String[] tempValueArr = null;
StringBuilder sb = new StringBuilder();
NodeFilter filter = null;
for(Iterator<String> it = tagMap.keySet().iterator(); it.hasNext();){
tempKey = it.next();
tempValue = tagMap.get(tempKey);
if(tempValue.contains("|")){
tempValueArr = tempValue.split("\\|");
}else{
tempValueArr = new String[]{tempValue};
}
for(String value : tempValueArr){
filter = populateFilter(tempKey,value);
appendHtmlByFilter(parser, filter, sb);
}
}
// 第二步过滤指定属性/标签内容
String contentHtml = sb.toString();
for (Iterator<String> it = removeTagMap.keySet().iterator(); it
.hasNext();) {
tempKey = it.next();
tempValue = removeTagMap.get(tempKey);
if(tempValue.contains("|")){
tempValueArr = tempValue.split("\\|");
}else{
tempValueArr = new String[]{tempValue};
}
for(String value : tempValueArr){
filter = populateFilter(tempKey,value);
contentHtml = removeHtmlByFilter(parser, filter, contentHtml);
}
}
//第三步过滤注释
filter = new NodeClassFilter(RemarkNode.class);
contentHtml = removeHtmlByFilter(parser, filter, contentHtml);
System.out.println("=================================结果=======================================");
System.out.println(contentHtml);
return contentHtml;
} catch (ParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
/**
* 解析并组装采集参数,支持标签属性/值形式和标签名称形式,可混合使用
*约定采集参数格式如下
*1,标签属性/值形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN
*2,标签名称形式,如:div,p,span
*3,混合形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN,div,p,span
* @param paramStr 参数字符串
*/
private Map<String, String> populateParamMap(String paramStr) {
Map<String, String> paramMap = new HashMap<String, String>();
String[] paramStrArr = paramStr.split(",");
String[] tempStrArr = null;
StringBuilder sb = new StringBuilder();
for(String temp : paramStrArr){
if(temp.contains("=")){
tempStrArr = temp.split("=");
paramMap.put(tempStrArr[0], tempStrArr[1]);
}else{
if(StringUtils.isNotEmpty(temp)){
sb.append(temp).append("|");
}
}
}
if(StringUtils.isNotEmpty(sb.toString())){
paramMap.put(SINGLE_TAG, sb.substring(0, sb.length() - 1));
}
return paramMap;
}
/**
* 组装过滤器
* @param key 键
* @param value 值
* @return 过滤器
*/
private NodeFilter populateFilter(String key,String value) {
NodeFilter filter;
if(SINGLE_TAG.equals(key)){
filter = new TagNameFilter(value);
}else{
filter = new HasAttributeFilter(key,value);
}
return filter;
}
/**
* 过滤指定属性标签HTML
* @param parser 解析器
* @param filter 属性过滤器
* @param orginHtml 原始HTML
* @return 过滤后HTML
* @throws ParserException
*/
private String removeHtmlByFilter(Parser parser, NodeFilter filter,String orginHtml) throws ParserException {
parser.setInputHTML(orginHtml);
NodeList nodes = parser.extractAllNodesThatMatch(filter);
for (int i = 0; i < nodes.size(); i++) {
Node textnode = (Node) nodes.elementAt(i);
orginHtml = StringUtils.remove(orginHtml, textnode.toHtml());
}
return orginHtml;
}
/**
* 取得所有指定属性/标签的HTML
* @param parser 解析器
* @param filter 过滤器
* @param sb
* @throws ParserException
*/
private void appendHtmlByFilter(Parser parser, NodeFilter filter,
StringBuilder sb) throws ParserException {
NodeList nodes = parser.extractAllNodesThatMatch(filter);
System.out.println("匹配节点数:" + nodes.size());
for (int i = 0; i < nodes.size(); i++) {
Node textnode = (Node) nodes.elementAt(i);
sb.append(textnode.toHtml());
}
}
/**
* 解析并组装采集参数,支持标签属性/值形式和标签名称形式,可混合使用
*约定采集参数格式如下
*1,标签属性/值形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN
*2,标签名称形式,如:div,p,span
*3,混合形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN,div,p,span
* @param paramMap 参数map
* @param str 参数字符串
*/
private void populateParamMap(Map<String, String> paramMap,String paramStr) {
String[] paramStrArr = paramStr.split(",");
String[] tempStrArr = null;
StringBuilder sb = new StringBuilder();
for(String temp : paramStrArr){
if(temp.contains("=")){
tempStrArr = temp.split("=");
paramMap.put(tempStrArr[0], tempStrArr[1]);
}else{
if(StringUtils.isNotEmpty(temp)){
sb.append(temp).append("|");
}
}
}
if(StringUtils.isNotEmpty(sb.toString())){
paramMap.put(SINGLE_TAG, sb.substring(0, sb.length() - 1));
}
}
/**
* 测试方法-打开文件并返回内容
* @param szFileName 文件绝对地址
* @param charset 字符集
* @return 内容
*/
public static String openFile(String szFileName,String charset) {
try {
BufferedReader bis = new BufferedReader(new InputStreamReader(
new FileInputStream(new File(szFileName)), charset));
StringBuilder szContent = new StringBuilder();
String szTemp;
while ((szTemp = bis.readLine()) != null) {
szContent.append(szTemp).append("\n");
}
bis.close();
return szContent.toString();
} catch (Exception e) {
return "";
}
}
/**
* 测试取得连接地址和标题
* @throws ParserException
*/
public void testFetchLinkAndTitle() throws ParserException{
String html = openFile("F:\\4.htm","UTF-8");
String result = "";
Map<String, String> map = new HashMap<String, String>();
map.put("class", "m_list");
Map<String, String> notMap = new HashMap<String, String>();
//notMap.put("class", "atc_ic_f");
result = getHtmlByFilter(map,notMap,html);
System.out.println("=============================result============================");
System.out.println(result);
System.out.println("==========================================================");
Pattern pt = Pattern.compile("<a.*href=\"(.*?)\".*>(.*?)</a>");
Matcher m = pt.matcher(result);
String link = null;
String title = null;
while (m.find()) {
link = m.group(1);
title = m.group(2);
if (StringUtils.isNotEmpty(link)) {
System.out.println("url : " + link);
System.out.println("title : " + title);
}
}
}
/**
* 测试取得内容
* @throws ParserException
*/
public void testFetchContent() throws ParserException{
String html = openFile("F:\\6.shtml","GB2312");
Map<String, String> map = new HashMap<String, String>();
map.put("id", "artibody");
Map<String, String> notMap = new HashMap<String, String>();
notMap.put(SINGLE_TAG, "style|script");
notMap.put("type", "text/javascript");
notMap.put("class", "icon_fx|blkComment otherContent_01");
notMap.put("style", "text-align: right;padding-right:10px;|margin-top:6px;|font-size: 12px ! important;|font-size:12px");
notMap.put("id", "fxwb|fxMSN|fxMSN|comment_t_show_top");
getHtmlByFilter(map,notMap,html);
}
/**
* 测试解析参数
*/
public void testParseParam(){
Map<String, String> map = new HashMap<String, String>();
populateParamMap(map,"class=articleList|tips,p,div");
String tempKey = null;
String tempValue = null;
String[] tempValueArr = null;
for (Iterator<String> it = map.keySet().iterator(); it.hasNext();) {
tempKey = it.next();
tempValue = map.get(tempKey);
if(tempValue.contains("|")){
tempValueArr = tempValue.split("\\|");
}else{
tempValueArr = new String[]{tempValue};
}
for(String value : tempValueArr){
System.out.println("tempKey:" + tempKey);
System.out.println("value:" + value);
}
}
}
/**
* 测试过滤标签
* @throws ParserException
*/
public void testRemarkFilter() throws ParserException{
String html = openFile("F:\\6.shtml","GB2312");
System.out.println("=========================过滤注释前HTML==================================");
System.out.println(html);
NodeFilter filter = new NodeClassFilter(RemarkNode.class);
html = removeHtmlByFilter(new Parser(), filter, html);
System.out.println("=========================过滤注释后HTML==================================");
System.out.println(html);
}
public static void main(String[] args) throws ParserException,
URISyntaxException, IOException {
HtmlParserImpl parseHtmlTool = new HtmlParserImpl(new CmsAcquisition());
//parseHtmlTool.testParseParam();
//parseHtmlTool.testFetchLinkAndTitle();
//parseHtmlTool.testFetchContent();
//parseHtmlTool.testRemarkFilter();
}
}
采集参数封装bean:
package com.jeecms.cms.service;
import java.util.HashMap;
import java.util.Map;
/**
* 采集参数封装bean
* @author javacoo
* @since 2011-10-31
*/
public class ParamBean {
/**待采集连接区域属性MAP*/
private Map<String, String> linksetStartMap = new HashMap<String, String>();
/**待采集连接区域过滤属性MAP*/
private Map<String, String> linksetEndMap = new HashMap<String, String>();
/**待采集内容区域属性MAP*/
private Map<String, String> contentStartMap = new HashMap<String, String>();
/**待采集内容区域过滤属性MAP*/
private Map<String, String> contentEndMap = new HashMap<String, String>();
public Map<String, String> getLinksetStartMap() {
return linksetStartMap;
}
public void setLinksetStartMap(Map<String, String> linksetStartMap) {
this.linksetStartMap = linksetStartMap;
}
public Map<String, String> getLinksetEndMap() {
return linksetEndMap;
}
public void setLinksetEndMap(Map<String, String> linksetEndMap) {
this.linksetEndMap = linksetEndMap;
}
public Map<String, String> getContentStartMap() {
return contentStartMap;
}
public void setContentStartMap(Map<String, String> contentStartMap) {
this.contentStartMap = contentStartMap;
}
public Map<String, String> getContentEndMap() {
return contentEndMap;
}
public void setContentEndMap(Map<String, String> contentEndMap) {
this.contentEndMap = contentEndMap;
}
}
AcquisitionSvcImpl类
package com.jeecms.cms.service;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jeecms.cms.entity.assist.CmsAcquisition;
import com.jeecms.cms.entity.main.Content;
import com.jeecms.cms.manager.assist.CmsAcquisitionMng;
@Service
public class AcquisitionSvcImpl implements AcquisitionSvc {
private Logger log = LoggerFactory.getLogger(AcquisitionSvcImpl.class);
public boolean start(Integer id) {
CmsAcquisition acqu = cmsAcquisitionMng.findById(id);
if (acqu == null || acqu.getStatus() == CmsAcquisition.START) {
return false;
}
Thread thread = new AcquisitionThread(acqu);
thread.start();
return true;
}
private CmsAcquisitionMng cmsAcquisitionMng;
@Autowired
public void setCmsAcquisitionMng(CmsAcquisitionMng cmsAcquisitionMng) {
this.cmsAcquisitionMng = cmsAcquisitionMng;
}
private class AcquisitionThread extends Thread {
private CmsAcquisition acqu;
/**HTML解析工具类*/
private ParseHtmlTool parseHtmlTool;
public AcquisitionThread(CmsAcquisition acqu) {
super(acqu.getClass().getName() + "#" + acqu.getId());
this.acqu = acqu;
parseHtmlTool = new HtmlParserImpl(acqu);
}
@Override
public void run() {
if (acqu == null) {
return;
}
long tStart = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "开始...");
acqu = cmsAcquisitionMng.start(acqu.getId());
String[] plans = acqu.getAllPlans();
//HttpHost proxy = new HttpHost("128.160.64.5", 1235);
HttpClient client = new DefaultHttpClient();
//client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,proxy);
CharsetHandler handler = new CharsetHandler(acqu.getPageEncoding());
List<String> urlList = null;
List<String> titleList = null;
String url = null;
String html = null;
int currNum = acqu.getCurrNum();
int currItem = acqu.getCurrItem();
Integer acquId = acqu.getId();
try {
for (int i = plans.length - currNum; i >= 0; i--) {
url = plans[i];
HttpGet httpget = new HttpGet(new URI(url.trim()));
html = client.execute(httpget, handler);
urlList = parseHtmlTool.getUrlList(html);
titleList = parseHtmlTool.getTitleList(html);
if (urlList != null) {
for (int j = urlList.size() - currItem; j >= 0; j--) {
if (cmsAcquisitionMng.isNeedBreak(acqu.getId(),
plans.length - i, urlList.size() - j,
urlList.size())) {
client.getConnectionManager().shutdown();
log.info("Acquisition#{} breaked", acqu.getId());
return;
}
if (acqu.getPauseTime() > 0) {
try {
Thread.sleep(acqu.getPauseTime());
} catch (InterruptedException e) {
log.warn("", e);
}
}
saveContent(client, handler, acquId, urlList.get(j), titleList.get(j));
}
}
currItem = 1;
}
} catch (Exception e) {
e.printStackTrace();
log.warn(null, e);
}
client.getConnectionManager().shutdown();
cmsAcquisitionMng.end(acqu.getId());
System.out.println(Thread.currentThread().getName() + "结束.");
long tEnd = System.currentTimeMillis();
System.out.println("总共用时:"+ (tEnd - tStart) + "millions");
log.info("Acquisition#{} complete", acqu.getId());
}
private Content saveContent(HttpClient client, CharsetHandler handler,Integer acquId, String url, String title) {
try {
HttpGet httpget = new HttpGet(new URI(url.trim()));;
String html = client.execute(httpget, handler);
String txt = parseHtmlTool.getHtml(html);
return cmsAcquisitionMng.saveContent(title, txt,acquId);
} catch (Exception e) {
log.warn(null, e);
e.printStackTrace();
return null;
}
}
}
private class CharsetHandler implements ResponseHandler<String> {
private String charset;
public CharsetHandler(String charset) {
this.charset = charset;
}
public String handleResponse(HttpResponse response)
throws ClientProtocolException, IOException {
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() >= 300) {
throw new HttpResponseException(statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}
HttpEntity entity = response.getEntity();
if (entity != null) {
if (!StringUtils.isBlank(charset)) {
return EntityUtils.toString(entity, charset);
} else {
return EntityUtils.toString(entity);
}
} else {
return null;
}
}
}
}
采集结果:
<div class="blkContainerSblkCon" id="artibody">
四川新闻网自贡10月28日讯 (记者 张贤明 黄国苗) 10月28日上午,自贡市第十一届委员会第一次全体会议选举产生新一届自贡市委常委。
会议以无记名投票方式,选举雷洪金为市委书记,彭琳、谭豹为市委副书记;向华全、陈吉明、姜怡、张乾华、李宪虎、曹俊杰、杨家禄、吴丕当选市委常委。
在自贡市纪委十一届一次全体会议上,张乾华当选为新一届自贡市纪委书记。
<div style="clear:both;height:0;visibility:hiddden;overflow:hidden;"></div>
</div>
用法:
在“开始HTML”处(没有修改模板,其实现在应该叫“内容区域参数”)写上你要采集的标签名称/标签属性及属性值,如 id=artibody
在“结束HTML”处(没有修改模板,其实现在应该叫“内容区域过滤参数”)写上在内容区域内你要过滤的标签名称/标签属性及属性 值,如:type=text/javascript,class=icon_fx|blkComment otherContent_01
约定:不同标签/属性以逗号(“,”)分隔,相同属性不同值,值与值之间以"|"分隔
1,标签属性/值形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN
2,标签名称形式,如:div,p,span
3,混合形式,如:class=articleList|tips,id=fxwb|fxMSN|fxMSN,div,p,span
采集参数样例:
新浪国内新闻:
内容地址集: 开始HTML: class=list_009 结束HTML :空
内容: 开始HTML: id=artibody 结束HTML :type=text/javascript,class=icon_fx|blkComment otherContent_01,style=text-align: right;padding-right:10px;|margin-top:6px;|font-size: 12px ! important;|font-size:12px,id=fxwb|fxMSN|fxMSN|comment_t_show_top,style,script
代码如下:
HTML解析工具类接口:
package com.jeecms.cms.service;
import java.util.List;
import com.jeecms.cms.entity.assist.CmsAcquisition;
/**
* HTML解析工具类接口
* @author javacoo
* @since 2011-10-31
*/
public interface ParseHtmlTool {
/**
* 取得连接集合
* @param orginHtml 原始HTML
* @return 连接集合
*/
List<String> getUrlList( String orginHtml);
/**
* 取得标题集合
* @param orginHtml 原始HTML
* @return 标题集合
*/
List<String> getTitleList(String orginHtml);
/**
* 取得指定区域的HTML内容
* @return 指定区域的HTML内容
*/
String getHtml(String orginHtml);
}
HTML解析工具,HtmlParser实现类:
package com.jeecms.cms.service;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.RemarkNode;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import com.jeecms.cms.entity.assist.CmsAcquisition;
/**
* HTML解析工具,HtmlParser实现类
* @author javacoo
* @since 2011-10-31
*/
public class HtmlParserImpl implements ParseHtmlTool{
/**连接集合标志*/
private static String LINK_LIST = "linkList";
/**标题集合标志*/
private static String TITLE_LIST = "titleList";
/**单标签标志*/
private static String SINGLE_TAG = "singleTag";
/**连接正则表达式*/
private static String LINK_REGX = "<a.*href=\"(.*?)\".*>(.*?)</a>";
/**正则表达式对象*/
private Pattern pt = Pattern.compile(LINK_REGX);
/**采集参数bean*/
private ParamBean paramBean;
public HtmlParserImpl(CmsAcquisition acqu){
parseRequestParam(acqu);
}
/**
* 取得标题集合
* @param orginHtml 原始HTML
* @return 标题集合
*/
public List<String> getTitleList(String orginHtml) {
orginHtml = getHtmlByFilter(paramBean.getLinksetStartMap(), paramBean.getLinksetEndMap(),orginHtml);
if (StringUtils.isNotEmpty(orginHtml)) {
return getUrlOrTitleListByType(orginHtml,TITLE_LIST);
}
return null;
}
/**
* 取得连接集合
* @param orginHtml 原始HTML
* @return 连接集合
*/
public List<String> getUrlList(String orginHtml) {
orginHtml = getHtmlByFilter(paramBean.getLinksetStartMap(), paramBean.getLinksetEndMap(),orginHtml);
if (StringUtils.isNotEmpty(orginHtml)) {
return getUrlOrTitleListByType(orginHtml,LINK_LIST);
}
return null;
}
/**
* 取得指定区域的HTML内容
* @param orginHtml 原始HTML
* @return 指定区域的HTML内容
* @throws ParserException
*/
public String getHtml(String orginHtml) {
orginHtml = getHtmlByFilter(paramBean.getContentStartMap(), paramBean.getContentEndMap(),orginHtml);
return orginHtml;
}
/**
* 解析采集参数,并封装到ParamBean
* @param acqu 原始采集参数
* @return 采集参数封装bean
*/
private void parseRequestParam(CmsAcquisition acqu){
paramBean = new ParamBean();
if(!StringUtils.isEmpty(acqu.getLinksetStart())){
paramBean.setLinksetStartMap(populateParamMap(acqu.getLinksetStart()));
}
if(!StringUtils.isEmpty(acqu.getLinksetEnd())){
paramBean.setLinksetEndMap(populateParamMap(acqu.getLinksetEnd()));
}
if(!StringUtils.isEmpty(acqu.getContentStart())){
paramBean.setContentStartMap(populateParamMap(acqu.getContentStart()));
}
if(!StringUtils.isEmpty(acqu.getContentEnd())){
paramBean.setContentEndMap(populateParamMap(acqu.getContentEnd()));
}
}
/**
* 得到地址集
* @param html html内容
* @param type 1 :取得连接集合,2:取得标题集合
* @return 连接或者标题集合
*/
private List<String> getUrlOrTitleListByType(String html, String type) {
List<String> resultList = new ArrayList<String>();
Matcher m = pt.matcher(html);
String result = "";
int pos = 1;
if(TITLE_LIST.equals(type)){
pos = 2;
}
while (m.find()) {
result = m.group(pos);
resultList.add(result);
}
return resultList;
}
/**
* 取得指定区域的HTML内容
* @param tagMap 标签MAP
* @param removeTagMap 要过滤的标签MAP
* @param orginHtml 原始HTML
* @return 指定区域的HTML内容
* @throws ParserException
*/
private String getHtmlByFilter(Map<String, String> tagMap,
Map<String, String> removeTagMap, String orginHtml) {
try {
Parser parser = new Parser();
parser.setInputHTML(orginHtml);
// 第一步取得指定属性/标签内容
String tempKey = null;
String tempValue = null;
String[] tempValueArr = null;
StringBuilder sb = new StringBuilder();
NodeFilter filter = null;
for(Iterator<String> it = tagMap.keySet().iterator(); it.hasNext();){
tempKey = it.next();
tempValue = tagMap.get(tempKey);
if(tempValue.contains("|")){
tempValueArr = tempValue.split("\\|");
}else{
tempValueArr = new String[]{tempValue};
}
for(String value : tempValueArr){
filter = populateFilter(tempKey,value);
appendHtmlByFilter(parser, filter, sb);
}
}
// 第二步过滤指定属性/标签内容
String contentHtml = sb.toString();
for (Iterator<String> it = removeTagMap.keySet().iterator(); it
.hasNext();) {
tempKey = it.next();
tempValue = removeTagMap.get(tempKey);
if(tempValue.contains("|")){
tempValueArr = tempValue.split("\\|");
}else{
tempValueArr = new String[]{tempValue};
}
for(String value : tempValueArr){
filter = populateFilter(tempKey,value);
contentHtml = removeHtmlByFilter(parser, filter, contentHtml);
}
}
//第三步过滤注释
filter = new NodeClassFilter(RemarkNode.class);
contentHtml = removeHtmlByFilter(parser, filter, contentHtml);
System.out.println("=================================结果=======================================");
System.out.println(contentHtml);
return contentHtml;
} catch (ParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "";
}
/**
* 解析并组装采集参数,支持标签属性/值形式和标签名称形式,可混合使用
*
*
*
*
* @param paramStr 参数字符串
*/
private Map<String, String> populateParamMap(String paramStr) {
Map<String, String> paramMap = new HashMap<String, String>();
String[] paramStrArr = paramStr.split(",");
String[] tempStrArr = null;
StringBuilder sb = new StringBuilder();
for(String temp : paramStrArr){
if(temp.contains("=")){
tempStrArr = temp.split("=");
paramMap.put(tempStrArr[0], tempStrArr[1]);
}else{
if(StringUtils.isNotEmpty(temp)){
sb.append(temp).append("|");
}
}
}
if(StringUtils.isNotEmpty(sb.toString())){
paramMap.put(SINGLE_TAG, sb.substring(0, sb.length() - 1));
}
return paramMap;
}
/**
* 组装过滤器
* @param key 键
* @param value 值
* @return 过滤器
*/
private NodeFilter populateFilter(String key,String value) {
NodeFilter filter;
if(SINGLE_TAG.equals(key)){
filter = new TagNameFilter(value);
}else{
filter = new HasAttributeFilter(key,value);
}
return filter;
}
/**
* 过滤指定属性标签HTML
* @param parser 解析器
* @param filter 属性过滤器
* @param orginHtml 原始HTML
* @return 过滤后HTML
* @throws ParserException
*/
private String removeHtmlByFilter(Parser parser, NodeFilter filter,String orginHtml) throws ParserException {
parser.setInputHTML(orginHtml);
NodeList nodes = parser.extractAllNodesThatMatch(filter);
for (int i = 0; i < nodes.size(); i++) {
Node textnode = (Node) nodes.elementAt(i);
orginHtml = StringUtils.remove(orginHtml, textnode.toHtml());
}
return orginHtml;
}
/**
* 取得所有指定属性/标签的HTML
* @param parser 解析器
* @param filter 过滤器
* @param sb
* @throws ParserException
*/
private void appendHtmlByFilter(Parser parser, NodeFilter filter,
StringBuilder sb) throws ParserException {
NodeList nodes = parser.extractAllNodesThatMatch(filter);
System.out.println("匹配节点数:" + nodes.size());
for (int i = 0; i < nodes.size(); i++) {
Node textnode = (Node) nodes.elementAt(i);
sb.append(textnode.toHtml());
}
}
/**
* 解析并组装采集参数,支持标签属性/值形式和标签名称形式,可混合使用
*
*
*
*
* @param paramMap 参数map
* @param str 参数字符串
*/
private void populateParamMap(Map<String, String> paramMap,String paramStr) {
String[] paramStrArr = paramStr.split(",");
String[] tempStrArr = null;
StringBuilder sb = new StringBuilder();
for(String temp : paramStrArr){
if(temp.contains("=")){
tempStrArr = temp.split("=");
paramMap.put(tempStrArr[0], tempStrArr[1]);
}else{
if(StringUtils.isNotEmpty(temp)){
sb.append(temp).append("|");
}
}
}
if(StringUtils.isNotEmpty(sb.toString())){
paramMap.put(SINGLE_TAG, sb.substring(0, sb.length() - 1));
}
}
/**
* 测试方法-打开文件并返回内容
* @param szFileName 文件绝对地址
* @param charset 字符集
* @return 内容
*/
public static String openFile(String szFileName,String charset) {
try {
BufferedReader bis = new BufferedReader(new InputStreamReader(
new FileInputStream(new File(szFileName)), charset));
StringBuilder szContent = new StringBuilder();
String szTemp;
while ((szTemp = bis.readLine()) != null) {
szContent.append(szTemp).append("\n");
}
bis.close();
return szContent.toString();
} catch (Exception e) {
return "";
}
}
/**
* 测试取得连接地址和标题
* @throws ParserException
*/
public void testFetchLinkAndTitle() throws ParserException{
String html = openFile("F:\\4.htm","UTF-8");
String result = "";
Map<String, String> map = new HashMap<String, String>();
map.put("class", "m_list");
Map<String, String> notMap = new HashMap<String, String>();
//notMap.put("class", "atc_ic_f");
result = getHtmlByFilter(map,notMap,html);
System.out.println("=============================result============================");
System.out.println(result);
System.out.println("==========================================================");
Pattern pt = Pattern.compile("<a.*href=\"(.*?)\".*>(.*?)</a>");
Matcher m = pt.matcher(result);
String link = null;
String title = null;
while (m.find()) {
link = m.group(1);
title = m.group(2);
if (StringUtils.isNotEmpty(link)) {
System.out.println("url : " + link);
System.out.println("title : " + title);
}
}
}
/**
* 测试取得内容
* @throws ParserException
*/
public void testFetchContent() throws ParserException{
String html = openFile("F:\\6.shtml","GB2312");
Map<String, String> map = new HashMap<String, String>();
map.put("id", "artibody");
Map<String, String> notMap = new HashMap<String, String>();
notMap.put(SINGLE_TAG, "style|script");
notMap.put("type", "text/javascript");
notMap.put("class", "icon_fx|blkComment otherContent_01");
notMap.put("style", "text-align: right;padding-right:10px;|margin-top:6px;|font-size: 12px ! important;|font-size:12px");
notMap.put("id", "fxwb|fxMSN|fxMSN|comment_t_show_top");
getHtmlByFilter(map,notMap,html);
}
/**
* 测试解析参数
*/
public void testParseParam(){
Map<String, String> map = new HashMap<String, String>();
populateParamMap(map,"class=articleList|tips,p,div");
String tempKey = null;
String tempValue = null;
String[] tempValueArr = null;
for (Iterator<String> it = map.keySet().iterator(); it.hasNext();) {
tempKey = it.next();
tempValue = map.get(tempKey);
if(tempValue.contains("|")){
tempValueArr = tempValue.split("\\|");
}else{
tempValueArr = new String[]{tempValue};
}
for(String value : tempValueArr){
System.out.println("tempKey:" + tempKey);
System.out.println("value:" + value);
}
}
}
/**
* 测试过滤标签
* @throws ParserException
*/
public void testRemarkFilter() throws ParserException{
String html = openFile("F:\\6.shtml","GB2312");
System.out.println("=========================过滤注释前HTML==================================");
System.out.println(html);
NodeFilter filter = new NodeClassFilter(RemarkNode.class);
html = removeHtmlByFilter(new Parser(), filter, html);
System.out.println("=========================过滤注释后HTML==================================");
System.out.println(html);
}
public static void main(String[] args) throws ParserException,
URISyntaxException, IOException {
HtmlParserImpl parseHtmlTool = new HtmlParserImpl(new CmsAcquisition());
//parseHtmlTool.testParseParam();
//parseHtmlTool.testFetchLinkAndTitle();
//parseHtmlTool.testFetchContent();
//parseHtmlTool.testRemarkFilter();
}
}
采集参数封装bean:
package com.jeecms.cms.service;
import java.util.HashMap;
import java.util.Map;
/**
* 采集参数封装bean
* @author javacoo
* @since 2011-10-31
*/
public class ParamBean {
/**待采集连接区域属性MAP*/
private Map<String, String> linksetStartMap = new HashMap<String, String>();
/**待采集连接区域过滤属性MAP*/
private Map<String, String> linksetEndMap = new HashMap<String, String>();
/**待采集内容区域属性MAP*/
private Map<String, String> contentStartMap = new HashMap<String, String>();
/**待采集内容区域过滤属性MAP*/
private Map<String, String> contentEndMap = new HashMap<String, String>();
public Map<String, String> getLinksetStartMap() {
return linksetStartMap;
}
public void setLinksetStartMap(Map<String, String> linksetStartMap) {
this.linksetStartMap = linksetStartMap;
}
public Map<String, String> getLinksetEndMap() {
return linksetEndMap;
}
public void setLinksetEndMap(Map<String, String> linksetEndMap) {
this.linksetEndMap = linksetEndMap;
}
public Map<String, String> getContentStartMap() {
return contentStartMap;
}
public void setContentStartMap(Map<String, String> contentStartMap) {
this.contentStartMap = contentStartMap;
}
public Map<String, String> getContentEndMap() {
return contentEndMap;
}
public void setContentEndMap(Map<String, String> contentEndMap) {
this.contentEndMap = contentEndMap;
}
}
AcquisitionSvcImpl类
package com.jeecms.cms.service;
import java.io.IOException;
import java.net.URI;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jeecms.cms.entity.assist.CmsAcquisition;
import com.jeecms.cms.entity.main.Content;
import com.jeecms.cms.manager.assist.CmsAcquisitionMng;
@Service
public class AcquisitionSvcImpl implements AcquisitionSvc {
private Logger log = LoggerFactory.getLogger(AcquisitionSvcImpl.class);
public boolean start(Integer id) {
CmsAcquisition acqu = cmsAcquisitionMng.findById(id);
if (acqu == null || acqu.getStatus() == CmsAcquisition.START) {
return false;
}
Thread thread = new AcquisitionThread(acqu);
thread.start();
return true;
}
private CmsAcquisitionMng cmsAcquisitionMng;
@Autowired
public void setCmsAcquisitionMng(CmsAcquisitionMng cmsAcquisitionMng) {
this.cmsAcquisitionMng = cmsAcquisitionMng;
}
private class AcquisitionThread extends Thread {
private CmsAcquisition acqu;
/**HTML解析工具类*/
private ParseHtmlTool parseHtmlTool;
public AcquisitionThread(CmsAcquisition acqu) {
super(acqu.getClass().getName() + "#" + acqu.getId());
this.acqu = acqu;
parseHtmlTool = new HtmlParserImpl(acqu);
}
@Override
public void run() {
if (acqu == null) {
return;
}
long tStart = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + "开始...");
acqu = cmsAcquisitionMng.start(acqu.getId());
String[] plans = acqu.getAllPlans();
//HttpHost proxy = new HttpHost("128.160.64.5", 1235);
HttpClient client = new DefaultHttpClient();
//client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,proxy);
CharsetHandler handler = new CharsetHandler(acqu.getPageEncoding());
List<String> urlList = null;
List<String> titleList = null;
String url = null;
String html = null;
int currNum = acqu.getCurrNum();
int currItem = acqu.getCurrItem();
Integer acquId = acqu.getId();
try {
for (int i = plans.length - currNum; i >= 0; i--) {
url = plans[i];
HttpGet httpget = new HttpGet(new URI(url.trim()));
html = client.execute(httpget, handler);
urlList = parseHtmlTool.getUrlList(html);
titleList = parseHtmlTool.getTitleList(html);
if (urlList != null) {
for (int j = urlList.size() - currItem; j >= 0; j--) {
if (cmsAcquisitionMng.isNeedBreak(acqu.getId(),
plans.length - i, urlList.size() - j,
urlList.size())) {
client.getConnectionManager().shutdown();
log.info("Acquisition#{} breaked", acqu.getId());
return;
}
if (acqu.getPauseTime() > 0) {
try {
Thread.sleep(acqu.getPauseTime());
} catch (InterruptedException e) {
log.warn("", e);
}
}
saveContent(client, handler, acquId, urlList.get(j), titleList.get(j));
}
}
currItem = 1;
}
} catch (Exception e) {
e.printStackTrace();
log.warn(null, e);
}
client.getConnectionManager().shutdown();
cmsAcquisitionMng.end(acqu.getId());
System.out.println(Thread.currentThread().getName() + "结束.");
long tEnd = System.currentTimeMillis();
System.out.println("总共用时:"+ (tEnd - tStart) + "millions");
log.info("Acquisition#{} complete", acqu.getId());
}
private Content saveContent(HttpClient client, CharsetHandler handler,Integer acquId, String url, String title) {
try {
HttpGet httpget = new HttpGet(new URI(url.trim()));;
String html = client.execute(httpget, handler);
String txt = parseHtmlTool.getHtml(html);
return cmsAcquisitionMng.saveContent(title, txt,acquId);
} catch (Exception e) {
log.warn(null, e);
e.printStackTrace();
return null;
}
}
}
private class CharsetHandler implements ResponseHandler<String> {
private String charset;
public CharsetHandler(String charset) {
this.charset = charset;
}
public String handleResponse(HttpResponse response)
throws ClientProtocolException, IOException {
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() >= 300) {
throw new HttpResponseException(statusLine.getStatusCode(),
statusLine.getReasonPhrase());
}
HttpEntity entity = response.getEntity();
if (entity != null) {
if (!StringUtils.isBlank(charset)) {
return EntityUtils.toString(entity, charset);
} else {
return EntityUtils.toString(entity);
}
} else {
return null;
}
}
}
}
采集结果:
<div class="blkContainerSblkCon" id="artibody">
四川新闻网自贡10月28日讯 (记者 张贤明 黄国苗) 10月28日上午,自贡市第十一届委员会第一次全体会议选举产生新一届自贡市委常委。
会议以无记名投票方式,选举雷洪金为市委书记,彭琳、谭豹为市委副书记;向华全、陈吉明、姜怡、张乾华、李宪虎、曹俊杰、杨家禄、吴丕当选市委常委。
在自贡市纪委十一届一次全体会议上,张乾华当选为新一届自贡市纪委书记。
<div style="clear:both;height:0;visibility:hiddden;overflow:hidden;"></div>
</div>
相关推荐
JEECMS 作为一个基于 Java 的内容管理系统,具有强大的功能和灵活的架构。阅读 JEECMS 的源码,可以了解到其内部的实现机制和设计理念。在本文中,我们将对 JEECMS 2.0 版本的源码进行阅读和分析,了解其核心组件、...
核心功能包的pojo com.jeecms.cms.entity. main.base 辅助功能包的pojo基础类 com.jeecms.cms.entity. main.hbm 辅助功能包Hibernate实体映射文件 com.jeecms.cms.entity.assist辅助功能包的pojo ...
在Jeecms3.0中,开发者们引入了一系列高级功能和优化,以提升用户体验和工作效率。 1. **模板引擎**:Jeecms3.0采用了先进的模板引擎,允许用户使用HTML和简单的标签语言来设计网站的布局和样式。这种模板系统极大...
在二次开发方面,jeecmsV6支持元件式无限插件扩展方式,开发者根据jeecms的插件开发标准所开发出来的插件,插件与jeecms是平行的,无论是插件升级还是jeecms系统升级,都可以实现独立升级,插件与插件、插件与jeecms...
《Jeecms学习笔记》是针对Java企业级内容管理系统Jeecms的学习资料,主要以guestbook功能为例,探讨了如何实现数据的写入操作。在这个过程中,开发者将深入理解Jeecms的架构、核心组件以及如何进行代码的修改与优化...
matlab算法,毕设、课设程序,全部源码均已进行严格测试,可以直接运行! matlab算法,毕设、课设程序,全部源码均已进行严格测试,可以直接运行! matlab算法,毕设、课设程序,全部源码均已进行严格测试,可以直接...
- **社区论坛**:通过扩展,Jeecms也能实现论坛交流功能,创建互动性强的社交平台。 4. **开发与部署** 开发者在使用Jeecms 9.2-adminVue时,需要具备Java和Vue.js基础,理解MVC架构和数据库操作。部署时,需要...
Jeecms是一款基于Java技术的企业级内容管理系统,它以其强大的功能、灵活的扩展性和易用性深受开发者喜爱。在实际项目中,Jeecms的二次开发是提升系统功能、满足特定业务需求的重要手段。本篇文章将深入探讨Jeecms的...
Jeecms内置了SEO优化功能,包括自定义URL、关键词设置、Meta标签等,有助于提高网站在搜索引擎中的排名,增加网站的可见度。 7. **移动适配与响应式设计** 针对现代互联网的多设备访问需求,Jeecms支持移动适配和...
通过阅读源码,我们可以深入理解Jeecms的工作原理,学习如何实现类似的功能,并且可能发现一些最佳实践或优化技巧。 最后,`DB`可能指的是数据库相关的文件或者目录,这可能包含数据库脚本、配置文件或者是数据库...
3. 自定义模板:Jeecms提供自定义模板功能,用户可以根据自己的需求设计和修改页面布局,实现个性化网站设计。 4. 高效缓存机制:内置高效的缓存机制,可以显著提升网站性能,降低服务器负载。 5. 强大的权限管理:...
Jeecms是一款基于Java技术开发的内容管理系统,专为企事业单位及个人提供快速、高效、稳定的网站建设和管理平台。源码包通常包含整个系统的全部代码,是开发者深入理解系统架构、功能实现以及进行二次开发的基础。...
8. **SEO优化**:作为网站构建工具,Jeecms通常会提供SEO(搜索引擎优化)功能,如自定义元标签、URL重写等,这些功能在源码中都有对应的实现。 9. **国际化与本地化**:Jeecms可能会支持多语言,源码中会有I18N...
在Web开发领域,JEECMS是一款基于Java EE平台的企业级内容管理系统,它提供了一套高效、稳定的网站构建解决方案。本配置文件"JEECMS站点群配置.zip"主要涉及了如何在Apache2服务器上通过JK模块与Tomcat6应用服务器...
同时,对于MVC模式的理解也是十分重要的,因为Jeecms就是这一模式的具体实现。 4. **开源特性** Jeecms的开源性质意味着开发者可以查看并修改其源代码,以满足特定的需求。这不仅提供了高度的定制性,也为社区贡献...
Jeecms是一款基于Java技术的开源内容管理系统,它以其高效、稳定、易用的特点,在国内IT行业中被广泛应用。本文将围绕“Jeecms文档”这一主题,深入解析Jeecms二次开发的相关知识点。 首先,Jeecms的核心特性在于其...
Jeecms是一款基于JSP技术的高效、灵活的内容管理框架,它允许开发者通过简单的接口来实现内容的发布、管理和展示,而无需深入理解复杂的后端技术。 Jeecms微信小程序的出现,使得Jeecms的功能得以延伸到移动端,...
4. SEO优化:Jeecms考虑到了搜索引擎优化的需求,提供了自定义关键词、描述等功能,帮助提高网站在搜索引擎中的排名。 5. 插件系统:Jeecms拥有强大的插件扩展机制,允许开发者根据需求开发各种功能插件,如统计...
Jeecms是一款基于Java技术的开源内容管理系统(Content Management System, CMS),广泛应用于企业网站、新闻门户等网站平台建设。其核心优势在于高度可定制化与模块化设计,使得开发者能够轻松地根据实际需求进行功能...
JEECMS,全称为Java Enterprise Edition Content Management System,是一款基于Java EE技术的开源内容管理系统。作为企业级的CMS解决方案,它具备高效、安全、易用、可扩展性强等特性,广泛应用于各类网站的建设和...