版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37358860/article/details/79085526
背景:
公司要做一个电商的网站,而该项目是由J2EE架构完成,项目经理说要让Java代码自助每天生成电子商务网站的Sitemap文件,然后开始上网各种查资料!!!
然而,终于碰上了本人有生以来第一个在网上没找到的具体答案的东西,自己干吧,不过网上也有做过类似的,只不过人家应该是大牛吧,没有说的很详细,只好自己慢慢领悟了.
按照大牛给的方向,摆在我面前一下出现三个问题:1,获取网站所有链接。 2,生成XML文件 3,定时调用,后两种问题如果做过Java程序的人,应该比较好解决。(这句话是大牛说的),这篇文章解决的是第一个问题和第二个问题,如何获得网站的所有链接并生成sitemap.xml文件
代码:
package com.langgufoeng.test.entity;
/**
* 标题: urlEntity.java
* 路径: com.langgufoeng.test.entity
* 描述: TODO sitemap.xml中每个链接对应的实体
* 作者: 郎国峰
* 时间: 2018-1-7 下午2:50:08
* 版本: @version V1.0
*/
public class UrlEntity {
private String loc;
private String lastmod;
private String changefreq;
private String priority;
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getLastmod() {
return lastmod;
}
public void setLastmod(String lastmod) {
this.lastmod = lastmod;
}
public String getChangefreq() {
return changefreq;
}
public void setChangefreq(String changefreq) {
this.changefreq = changefreq;
}
public String getPriority() {
return priority;
}
public void setPriority(String priority) {
this.priority = priority;
}
@Override
public String toString() {
return "UrlEntity [loc=" + loc + ", lastmod=" + lastmod
+ ", changefreq=" + changefreq + ", priority=" + priority + "]";
}
}
package com.langgufoeng.test.entity;
import java.util.List;
/**
* 标题: UrlsetEntity.java
* 路径: com.langgufoeng.test.entity
* 描述: TODO sitemap.xml对应的实体
* 作者: 郎国峰
* 时间: 2018-1-7 下午2:51:15 版本: @version V1.0
*/
public class UrlsetEntity {
static List<UrlEntity> list;
public static List<UrlEntity> getList() {
return list;
}
public static void setList(List<UrlEntity> list) {
UrlsetEntity.list = list;
}
}
package com.langgufoeng.test;
import java.io.File;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.List;
import com.langgufoeng.test.entity.UrlEntity;
import com.langgufoeng.test.entity.UrlsetEntity;
import com.redfin.sitemapgenerator.ChangeFreq;
import com.redfin.sitemapgenerator.WebSitemapGenerator;
import com.redfin.sitemapgenerator.WebSitemapUrl;
/**
* 标题: EntityToSitemap.java
* 路径: com.langgufoeng.test
* 描述: TODO 根据实体类生成sitemap
* 作者: 郎国峰
* 时间: 2018-1-7 下午3:00:01
* 版本: @version V1.0
*/
public class EntityToSitemap {
static public void toSitemap(){
WebSitemapGenerator sitemapGenerator = null;
try {
// 压缩输出 true 不压缩输出false
sitemapGenerator = WebSitemapGenerator.builder("http://www.baidu.com", new File("WebRoot")).gzip(false).build();
//得到sitemap实体
List<UrlEntity> sitemapList = UrlsetEntity.getList();
//遍历实体,得到sitemapUrl信息
for (int i = 0; i < sitemapList.size(); i++) {
UrlEntity sitemap = sitemapList.get(i);
System.out.println("url路径"+sitemap.getLoc());
WebSitemapUrl sitemapUrl = new WebSitemapUrl.Options(sitemap.getLoc()).build();
sitemapGenerator.addUrl(sitemapUrl);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
sitemapGenerator.write();
}
}
}
package com.langgufoeng.test;
import java.util.HashSet;
import java.util.Set;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.filters.OrFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
/**
* 标题: HtmlParserTool.java
* 路径: com.langgufoeng.test
* 描述: TODO 从获得的网页中提取url
* 作者: 郎国峰
* 时间: 2018-1-7 上午10:32:37
* 版本: @version V1.0
*/
public class HtmlParserTool {
private HtmlParserTool(){}
private static final HtmlParserTool htmlParserTool = new HtmlParserTool();
/*
* 单例模式
*/
public static HtmlParserTool getInstance() {
return htmlParserTool;
}
/**
* @方法名: extracLinks
* @描述: 获取一个网站上的链接,filter用来过滤链接
* @作者: 郎国峰
* @时间: 2018-1-7 上午10:48:51
* @return 返回页面上解析出来的集合
*/
public static Set<String> extracLinks(String url, LinkFilter filter) {
//创建一个set集合,用来存储页面上解析出来的符合标准的url
Set<String> links = new HashSet<String>();
try {
Parser parser = new Parser(url);
parser.setEncoding("UTF-8");
//System.out.println("根据url得到的解析结果"+parser);
//过滤<frame> 标签的filter
NodeFilter frameFilter = new NodeFilter() {
public boolean accept(Node node) {
if (node.getText().startsWith("frame src=")){
return true;
}
return false;
}
};
//OrFilter 用来设置过滤<a> 标签和<frame> 标签
OrFilter linkFilter = new OrFilter(new NodeClassFilter(LinkTag.class), frameFilter);
//得到所有经过过滤的标签
NodeList list = parser.extractAllNodesThatMatch(linkFilter);
//System.out.println("所有经过过滤的标签"+list+"\t list长度:"+list.size());
for (int i = 0; i < list.size(); i++) {
Node tag = list.elementAt(i);
//System.out.println(tag+"每一个标签");
//判断是否是 <a> 标签
if (tag instanceof LinkTag) {
LinkTag link = (LinkTag) tag;
//得到a标签的href
String linkUrl = link.getLink();
//判断a标签的href的值是否是本网站的,如果是就添加到links中
if (filter.accept(linkUrl)){
links.add(linkUrl);
}
} else {//<frame>标签
//提取frame里的src属性的链接, 如 <frame src="test.html"/>
String frame = tag.getText();
int start = frame.indexOf("src=");
if (start != -1) {
frame = frame.substring(start);
}
int end = frame.indexOf(" ");
String frameUrl = "";
if (end == -1) {
end = frame.indexOf(">");
if (end - 1 > 5) {
frameUrl = frame.substring(5, end - 1);
}
}
if (filter.accept(frameUrl)) {
links.add(frameUrl);
}
}
}
} catch (ParserException e) {
e.printStackTrace();
}
return links;
}
}
package com.langgufoeng.test;
/**
* 标题: LinkFilter.java
* 路径: com.langgufoeng.test
* 描述: TODO 定义一个网址过滤器,判断是否以固定网址开头
* 作者: 郎国峰
* 时间: 2018-1-8 下午5:05:47
* 版本: @version V1.0
*/
public interface LinkFilter {
public boolean accept(String url);
}
package com.langgufoeng.test;
import java.util.HashSet;
import java.util.Set;
/**
* 标题: LinkQueue.java
* 路径: com.langgufoeng.test
* 描述: TODO 除了URL队列之外,在爬虫过程中,还需要一个数据结构来记录已经访问过的URL.
* 每当访问一个URL的时候,首先在这个数据结构中进行查找,如果当前URL已经存在,则丢
* 弃它,这个数据结构要有两个特点:
* ~ 结构中保存的URL不能重复
* ~ 能够快速的查找(实际系统中URL的数目非常多,因此要考虑查找性能).
* 针对以上两点,我们选择HashSet作为存储结构
* 作者: 郎国峰
* 时间: 2018-1-7 上午8:58:04
* 版本: @version V1.0
*/
public class LinkQueue {
//已访问的url集合
private static Set visitedUrl = new HashSet();
//待访问的url集合
private static Queue unVisitedUrl = new Queue();
//单例
private final static LinkQueue linkQueue = new LinkQueue();
private LinkQueue(){}
public static LinkQueue getInstance(){
return linkQueue;
}
/**
* @方法名: getUnVisitedUrl
* @描述: 获得将要访问的URL队列
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:06:21
* @return
*/
public static Queue getUnVisitedUrl(){
return unVisitedUrl;
}
/**
* @方法名: addVisitedUrl
* @描述: 添加到访问过的URL队列中
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:07:47
* @param url
*/
public static void addVisitedUrl(String url){
visitedUrl.add(url);
}
/**
* @方法名: removeVisitedUrl
* @描述: 移出访问过的url
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:09:40
* @param url
*/
public static void removeVisitedUrl(String url){
visitedUrl.remove(url);
}
/**
* @方法名: unVisitedUrlDeQueue
* @描述: 未访问过的url出队列
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:12:20
* @return
*/
public static Object unVisitedUrlDeQueue(){
return unVisitedUrl.deQueue();
}
/**
* @方法名: addUnvisitedUrl
* @描述: 保证每个url只被访问一次
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:19:26
* @param url
*/
public static void addUnvisitedUrl(String url){
if(url != null && !url.trim().equals("") //url不为null也不为空
&& !visitedUrl.contains(url) //url未被访问过
&& !unVisitedUrl.contians(url)){ //url队列里不包含此url
unVisitedUrl.enQueue(url); //将此url添加到url队列中
}
}
/**
* @方法名: getVisitedUrlNum
* @描述: 获得已经访问过的URL数目
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:21:02
* @return
*/
public static int getVisitedUrlNum(){
return visitedUrl.size();
}
/**
* @方法名: unVisitedUrlsEmpty
* @描述: 判断未访问过的url是否为空
* @作者: 郎国峰
* @时间: 2018-1-7 上午9:24:03
* @return
*/
public static boolean unVisitedUrlsEmpty(){
return unVisitedUrl.isQueueEmpty();
}
/**
* @方法名: getVisitedUrl
* @描述: 获得已经访问过的URL集合
* @作者: 郎国峰
* @时间: 2018-1-7 下午3:23:46
* @return
*/
public static Set getVisitedUrl() {
return visitedUrl;
}
}
package com.langgufoeng.test;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.langgufoeng.test.entity.UrlEntity;
import com.langgufoeng.test.entity.UrlsetEntity;
/**
* 标题: MainCrawler.java
* 路径: com.langgufoeng.test
* 描述: TODO 生成sitemap.xml主程序
* 作者: 郎国峰
* 时间: 2018-1-7 下午1:52:36
* 版本: @version V1.0
*/
public class MainCrawler {
private LinkQueue linkQueue = LinkQueue.getInstance();
/**
* @方法名: initCrawlerWithSeeds
* @描述: 使用种子初始化RUL队列
* @作者: 郎国峰
* @时间: 2018-1-7 下午1:53:13
* @param seeds
*/
private void initCrawlerWithSeeds(String[] seeds) {
for(int i=0; i<seeds.length; i++) {
linkQueue.addUnvisitedUrl(seeds[i]);
}
}
/**
* @方法名: crawling
* @描述: 根据提供的种子进行抓取所有页面
* @作者: 郎国峰
* @时间: 2018-1-7 下午1:56:17
* @param seeds
*/
public void crawling(String[] seeds) {
//定义过滤器,提取以 www.baidu.com 开始的链接
LinkFilter filter = new LinkFilter() {
public boolean accept(String url) {
if(url.startsWith("http://www.baidu.com")) {
return true;
}
return false;
}
};
int i = 0;
//初始化URL队列
initCrawlerWithSeeds(seeds);
//循环抓取页面 循环条件:待抓取的链接不空且抓取的页面不超过1000个
while(!linkQueue.unVisitedUrlsEmpty() && linkQueue.getVisitedUrlNum()<=1000) {
//队头URL出队列
String visitUrl = (String) linkQueue.unVisitedUrlDeQueue();
if(visitUrl == null) { //如果没有未被访问过的url,将终止循环
continue;
}
System.out.println("url:"+visitUrl);
DownLoadFile downLoader = new DownLoadFile();
//下载网页
//downLoader.downloadFile(visitUrl);
//将该url放入已访问的url中
linkQueue.addVisitedUrl(visitUrl);
//提取出下载网页中的url
Set<String> links = HtmlParserTool.extracLinks(visitUrl, filter);
System.out.println("再次提取的页面路径"+links);
for(String link:links) {
linkQueue.addUnvisitedUrl(link);
}
System.err.println("-------------------------------循环"+ ++i+"次------------------------------");
}
}
public static void main(String[] args) {
//声明一个网站地址字符串
//创建一个爬虫主线程
MainCrawler crawler = new MainCrawler();
crawler.crawling(new String[]{"http://www.baidu.com"}); //NewFile main
//将所有访问过的链接放到一个set里
Set set = LinkQueue.getVisitedUrl();
//创建一个list用来存储sitemap信息
List<UrlEntity> list = new ArrayList<UrlEntity>();
//遍历所有访问过的链接,得到对应的sitemap信息,赋值给url实体
for (Object object : set) {
UrlEntity url = new UrlEntity();
url.setLoc(object.toString());
list.add(url);
}
//将url实体集合赋值给urlset实体
UrlsetEntity.setList(list);
//创建sitemap文件
EntityToSitemap.toSitemap();
}
}
package com.langgufoeng.test;
import java.util.LinkedList;
/**
* 标题: Queue.java
* 路径: com.langgufoeng.test
* 描述: TODO 队列,保存将要访问的URL
* 作者: 郎国峰
* 时间: 2018-1-7 上午8:49:29
* 版本: @version V1.0
*/
public class Queue {
//使用链表实现队列
private LinkedList queue = new LinkedList();
/**
* @方法名: enQueue
* @描述: 入队列
* @作者: 郎国峰
* @时间: 2018-1-7 上午8:52:02
* @param t
*/
public void enQueue(Object t){
queue.addLast(t);
}
/**
* @方法名: deQueue
* @描述: 出队列
* @作者: 郎国峰
* @时间: 2018-1-7 上午8:53:13
* @return
*/
public Object deQueue(){
return queue.removeFirst();
}
/**
* @方法名: isQueueEmpty
* @描述: 判断队列是否为空
* @作者: 郎国峰
* @时间: 2018-1-7 上午8:54:17
* @return
*/
public boolean isQueueEmpty(){
return queue.isEmpty();
}
/**
* @方法名: contians
* @描述: 判断队列是否包含t
* @作者: 郎国峰
* @时间: 2018-1-7 上午8:55:35
* @param t
* @return
*/
public boolean contians(Object t){
return queue.contains(t);
}
}
注意:
以上代码是我自己写的一个例子,用的是myeclipse,测试的百度首页,解析的连接知识a标签和frame标签的连接,如果项目需要可以参考,然后按实际需求进行相应的更改