自动生成网站地图,无非就是下载一个页面,提取里边所有的链接,然后对提取到的链接地址做同样的操作,直到遍历完所有的链接。
下载页面比较简单,用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属性取到了,但是不一定就是链接,或者是链接但是是相对地址(我用是否包含“://”来判断是先对地址还是绝对地址)。所以需要进一步处理,去掉那些明显不是链接的,把相对地址改为绝对地址。
如何把相对地址改为绝对地址呢?当前页面的地址是知道的,只需要把当前页面的地址和相对地址连接到一起即可。这样有可能会出现这种地址" /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"); } } } }
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>这句就可以了。
参考:http://blog.csdn.net/huzhengnan/article/details/22883383
as3htmlparser,一个as3解析html的类库:
http://as3htmlparser.sourceforge.net/
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。