天天看点

SEO优化之根据网址自动生成sitemap.xml文件

背景:

公司要做一个电商的网站,而该项目是由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标签的连接,如果项目需要可以参考,然后按实际需求进行相应的更改,可能存在一定问题,欢迎批评指正.

继续阅读