自动生成网站地图,无非就是下载一个页面,提取里边所有的链接,然后对提取到的链接地址做同样的操作,直到遍历完所有的链接。
下载页面比较简单,用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");
}
}
}
}
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/
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。