25
2015
08

制作网站地图生成工具



自动生成网站地图,无非就是下载一个页面,提取里边所有的链接,然后对提取到的链接地址做同样的操作,直到遍历完所有的链接。

下载页面比较简单,用URLLoader即可。难点是页面下载下来之后,提取里面所有的链接。总结一下遇到的问题:

1、页面编码。

html页面其实就是一个文本文件,我的页面都是utf-8编码的,但是其他页面的编码格式就不确定了,解码错误的话,就会出现乱码,尤其是中文内容。有一款免费的网站地图生成工具SitemapX就是中文乱码。

2、提取链接

这个是最难的。首先,html中链接的存在方式有哪些?或者链接有什么共同的特征?我们不能通过后缀名来区分,因为诸如"index.html"是可以省略的,就像我们平时访问百度时,地址栏是“ww.baidu.com”,后面并没有页面的名字。html中的a标签是可以添加链接的,我们暂时忽略其他情况,暂且认为所有的链接都是通过a标签添加的,这样,我们只需要提取所有的a标签就可以了。

如果有解析html的类,我们可以先解析html,再提取里面的a标签,这样就比较方便了。或者可以用正则表达式来匹配。

as中没有解析html的方法,所以我用的是正则表达式的方法。(匹配的是否准确,就看正则表达式怎么写了,这点可以自己研究)

匹配到a标签以后,提取里边的href属性,这点还是比较简单的(单纯的a标签可以当做xml来处理,好像在哪里看过:html是xml的一个子集)。

href属性取到了,但是不一定就是链接,或者是链接但是是相对地址(我用是否包含“://”来判断是先对地址还是绝对地址)。所以需要进一步处理,去掉那些明显不是链接的,把相对地址改为绝对地址。

如何把相对地址改为绝对地址呢?当前页面的地址是知道的,只需要把当前页面的地址和相对地址连接到一起即可。这样有可能会出现这种地址" http://hanyeah.com/works.html/../works.html  ",这种情况也需要处理。

处理完之后,还要判断是否和初始的页面在同一个域名,以及是否重复。


代码:

package  {
	
	import com.bit101.components.CheckBox;
	import com.bit101.components.ComboBox;
	import com.bit101.components.Label;
	import com.bit101.components.Style;
	import flash.display.MovieClip;
	import flash.display.SimpleButton;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.SecurityErrorEvent;
	import flash.geom.Rectangle;
	import flash.net.FileReference;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.system.System;
	import flash.text.TextField;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	import flash.utils.unescapeMultiByte;
	import flash.utils.escapeMultiByte;
	
	public class Main extends MovieClip {
		
		public var tf:TextField;
		public var tf2:TextField;
		public var tf3:TextField;
		public var tf4:TextField;
		public var outTf:TextField;
		public var zqBtn:SimpleButton;
		public var saveBtn:SimpleButton;
		public var mc:MovieClip;
		
		private var uld:URLLoader = new URLLoader();
		private var successUrls:Array=[];
		private var errorUrls:Array;
		private var currentUrl:Object;
		private var homeUrl:String;
		private var urlStack:Vector.<Object>;
		private var allUrl:Vector.<String>;
		private var comboboxArr:Array = [];
		private var comboFreq:ComboBox;
		private var _n:int = 0;
		private var checkBox:CheckBox;
		public function Main() {
			// constructor code
			System.useCodePage = true;
			stage?initStage(null):addEventListener(Event.ADDED_TO_STAGE, initStage);
		}
		
		private function initStage(e:Event):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, initStage);
			
			Style.fontSize = 12;
			Style.embedFonts = false;
			Style.fontName = "Microsoft YaHei";
			var i:int = 0;
			var arr:Array = [];
			for (i = 10; i >0;i-- ) {
				arr.push(i/10);
			}
			for (i = 0; i < 5;i++ ) {
				var co:ComboBox = new ComboBox(this, 40 + 80 * i, 560, "", arr);
				co.selectedIndex = i * 2;
				var label:Label = new Label(this, 40 + 80 * i, 540, "第" + (i + 1) + "级权重");
				label.width = 60;
				co.openPosition = ComboBox.TOP;
				co.width = 60;
				comboboxArr.push(co);
			}
			var labelFreq:Label = new Label(this, 500, 540, "更新频率");
			comboFreq = new ComboBox(this, 500, 560, "", ["Always", "Hourly", "Daily", "Weekly", "Monthly", "Yearly", "Never"]);
			comboFreq.selectedIndex = 3;
			comboFreq.openPosition = ComboBox.TOP;
			
			checkBox = new CheckBox(this, 660, 585, "包含title");
			
			uld.addEventListener(Event.COMPLETE, onPageLoaded);
			uld.addEventListener(IOErrorEvent.IO_ERROR, onError);
			uld.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
			
			zqBtn.addEventListener(MouseEvent.CLICK, zhuaqu);
			saveBtn.addEventListener(MouseEvent.CLICK, save);
			
			mc.buttonMode = true;
			mc.addEventListener(MouseEvent.MOUSE_DOWN, mcMouseHandler);
			
		}
		
		private function mcMouseHandler(e:Event):void 
		{
			switch(e.type) {
				case MouseEvent.MOUSE_DOWN:
					mc.startDrag(false, new Rectangle(mc.x, 67, 0, 434));
					stage.addEventListener(MouseEvent.MOUSE_UP, mcMouseHandler);
					addEventListener(Event.ENTER_FRAME, mcMouseHandler);
					break;
				case MouseEvent.MOUSE_UP:
					mc.stopDrag();
					stage.removeEventListener(MouseEvent.MOUSE_UP, mcMouseHandler);
					removeEventListener(Event.ENTER_FRAME, mcMouseHandler);
					break;
				case Event.ENTER_FRAME:
					var per:Number = (mc.y - 67) / (434 - 67);
					var scrollV:int = per * outTf.maxScrollV;
					outTf.scrollV = scrollV;
					break;
			}
			
		}
		/**
		 * 保存
		 * @param	e
		 */
		private function save(e:MouseEvent):void 
		{
			var file:FileReference = new FileReference();
			var xml:XML = new XML('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset>');
			xml.ignoreComments = false;
			var urlModel:XML=<url>
									<loc>http:\/\/hanyeah.com/ </loc>
									<priority>0.8</priority>
									<lastmod>2015-08-24T05:40:42+00:00</lastmod>
									<changefreq>Weekly</changefreq>
								</url>
			var i:int;
			var len:int = successUrls.length;
			var fre:String = comboFreq.selectedItem+""; trace(fre);
			var priorityArr:Array = [];
			for (i = 0; i < comboboxArr.length;i++ ) {
				priorityArr.push(comboboxArr[i].selectedItem+""); trace(priorityArr[i]);
			}
			for (i = 0; i < len;i++ ) {
				var obj:Object = successUrls[i];
				var dep:int = obj.dep;
				var urlXML:XML = urlModel.copy();
				urlXML.loc[0] = obj.url;					//网址
				urlXML.priority[0] = priorityArr[dep];		//权重
				urlXML.changefreq[0] = fre;					//更新频率
				urlXML.lastmod[0] = new Date().toString();	//更新时间(不知道怎么获取页面的更新时间)
				if(checkBox.selected)urlXML.appendChild(XML("<title>"+obj.title+"</title>")) ;	
				xml.appendChild(urlXML);
			}
			
			file.save('<?xml version="1.0" encoding="UTF-8"?>\n<!-- Free Sitemap Generator http://www.hanyeah.com -->\n'+xml.toXMLString(), "sitemap.xml");
		}
		/**
		 * 开始抓取网页
		 * @param	e
		 */
		private function zhuaqu(e:MouseEvent):void 
		{
			if (tf.text == "") return;
			_n = 0;
			homeUrl = tf.text;
			errorUrls = [];
			successUrls = [];
			urlStack = new Vector.<Object>();
			urlStack.push( { "url":homeUrl, "dep":0 } );
			allUrl = new Vector.<String>();
			allUrl.push(homeUrl);
			outTf.text = "";
			tf2.text = tf3.text = tf4.text = "000";
			downLoadApage();
		}
		/**
		 * 下载页面出错
		 * @param	e
		 */
		private function onError(e:Event):void 
		{
			errorUrls.push(currentUrl);
			outTf.appendText("抓取失败\n");
			//
			tf4.text = errorUrls.length + "";
			//
			downLoadApage();
		}
		
		var pattern:RegExp = new RegExp("<a.*?>", "g");
		var patternTitle:RegExp = new RegExp("<title.*?/title>", "g");
		/**
		 * 页面下载成功
		 * @param	e
		 */
		private function onPageLoaded(e:Event):void 
		{
			successUrls.push(currentUrl);
			outTf.appendText("抓取成功\n");
			//判断编码
			var html:String = "";
			var byte:ByteArray = (uld.data as ByteArray);
			html = byte.readMultiByte(byte.bytesAvailable, "utf-8"); 
			
			var titleArr:Array = html.match(patternTitle);
			if (titleArr) {
				var ts:String = titleArr[0];
				var titleXML:XML = XML(ts);
				currentUrl.title = titleXML.toString();
			}
			/*
			byte.position = 0;
			var f1:uint = byte.readUnsignedShort();
			byte.position = 0;
			var html:String = "";
			//trace(f1.toString(16));
			if (f1 == 0xfffe) {//Unicode的小尾编码
				byte.endian = Endian.LITTLE_ENDIAN;
				html = byte.readMultiByte(byte.bytesAvailable, "unicode");  
				//trace("Unicode的小尾编码");
			}
			else if (f1==0xfeff) {//Unicode的大尾编码
				byte.endian = Endian.BIG_ENDIAN;
				html = byte.readMultiByte(byte.bytesAvailable, "unicode");  
				//trace("Unicode的大尾编码");
			}
			else if (f1==0xefbb) {//Unicode的UTF-8编码
				html = byte.readMultiByte(byte.bytesAvailable, "utf-8");  
				//trace("Unicode的UTF-8编码");
			}
			else {//ANSI编码
				html = byte.readMultiByte(byte.bytesAvailable, "utf-8"); 		
				//trace("ANSI编码");
				//trace(html);
			}
			*/
			var arr:Array = html.match(pattern);
			var i:int = 0;
			if (arr) {
				var len:int = arr.length;
				var dep:int = currentUrl.dep;
				for (i = 0; i < len; i++ ) {					
					var s:String = arr[i];
					s.replace("/>", ">");
					s += "</a>";
					try {
						var xml:XML = XML(s);
						var url:String = format(xml.@href);
						
						if (url && url != "" && (url.indexOf(homeUrl) >= 0)) {
							//trace(url);
							allUrl.push(url);////用push还是unshift?
							urlStack.push( { "url":url, "dep":dep + 1 } );
							//trace(i+"/"+len+":"+url);
						}
					}
					catch (e:Error) {
						trace("========="+currentUrl.url)
						trace(s + "======" + e);
					}
					
				}
			}
			//
			tf3.text = successUrls.length + "";
			//
			downLoadApage();
		}
		var ignorHouzhui:Array = [".rar", ".swf", ".apk", ".js", ".air", "javascript:"];
		var houzhui:Array = [".htm",".php",".jsp",".asp",".asp"];
		private function format(s:String):String {
			var s1:String = "";
			if (s.length < 1) return "";
			if (s.charAt(0) == "#") return "";
			
			var i:int;
			var len:int;
			
			len= ignorHouzhui.length;
			for (i = 0; i < len;i++ ) {
				if (s.indexOf(ignorHouzhui[i]) >= 0) {
					return "";
					break;
				}
			}
			// 将相对地址添加完整 
			if (s.indexOf("://") == -1) {
				var url:String = currentUrl.url;
				//去掉get参数
				url = url.split("?")[0];
				len= houzhui.length;
				for (i = 0; i < len;i++ ) {
					if (url.indexOf(houzhui[i]) > 0) {
						url=url.slice(0, url.lastIndexOf("/"))
						break;
					}
				}
				//
				if (url.charAt(url.length-1)=="/") {
					url = url.substring(0, url.length-1);
				}
				if (s.charAt(0)=="/") {
					s = s.substring(1);
				}
				s1 = url +"/" + s;
				//匹配字符串 "*/../"
				var pattern2:RegExp = new RegExp("((?!/).)*/\.\.?/", "g");
				s1=s1.replace(pattern2,"");
			}
			else {
				s1 = s;
			}
			//检测是否重复
			len = allUrl.length;
			for (i = 0; i < len;i++ ) {
				if (allUrl[i]==s1) {
					return "";
				}
			}
			//
			return s1;
		}
		private function downLoadApage():void {
			_n++;			
			outTf.appendText(_n+"================================\n");
			currentUrl = urlStack.shift();//用shift还是pop?
			if (currentUrl) {
				tf2.text = "" + _n;
				outTf.appendText(currentUrl.url+"\n");
				outTf.appendText("开始抓取\n");
				uld.dataFormat = URLLoaderDataFormat.BINARY;
				uld.load(new URLRequest(currentUrl.url));
			}
			else {
				outTf.appendText("结束\n");
			}
		}
	}
	
}
获得 Adobe Flash Player



2015-09-30修改:

file.save('<?xml version="1.0" encoding="UTF-8"?>\n<?xml-stylesheet type="text/xsl" href="sitemap.xsl"?>\n<!-- Free Sitemap Generator http://www.hanyeah.com -->\n'+xml.toXMLString(), "sitemap.xml");

改为file.save('<?xml version="1.0" encoding="UTF-8"?>\n<!-- Free Sitemap Generator http://www.hanyeah.com -->\n'+xml.toXMLString(), "sitemap.xml");

去掉了<?xml-stylesheet type="text/xsl" href="sitemap.xsl"?>

不知道这句是什么作用,加上这句,通过浏览器访问sitemap.xml时会出问题。

提交到百度的话,会报错,去掉<ignoreComments>false</ignoreComments>这句就可以了。



« 上一篇下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。