天天看點

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标簽的連接配接,如果項目需要可以參考,然後按實際需求進行相應的更改,可能存在一定問題,歡迎批評指正.

繼續閱讀