代码:
System.useCodepage = true;
myLoadVars = new LoadVars();
function lrcSearch(keyWord) {
musicName = keyWord.slice(0, keyWord.indexOf(" "));
myLoadVars.load("http://www.baidu.com/s?wd="+"[ti:"+keyWord);
myLoadVars.onData = getLink;
pageIndex = 1;
}
function getLink(src) {
nextPageLink = "http://www.baidu.com/"+src.slice(src.lastIndexOf("href=", src.indexOf("下一页"))+5, src.lastIndexOf("><", src.indexOf("下一页")));
linkData = src.split("快照");
for (var i = 0; i linkData[i] = linkData[i].slice(0, linkData[i].lastIndexOf(" linkData[i] = linkData[i].slice(linkData[i].lastIndexOf("href=")+6);
linkData[i] = linkData[i].slice(0, linkData[i].indexOf("\""));
}
linkIndex = 1;
openLRCPage(linkData[0]);
}
function openLRCPage(url) {
myLoadVars.load(url);
myLoadVars.onData = getLRC;
}
function getLRC(src) {
htmlData = src.slice(src.lastIndexOf("[ti:"+musicName));
findEndTag();
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
lrcData = htmlData.split(String.fromCharCode(10)).join("");
tx_lrc.text = lrcData;
}
function findEndTag() {
var lastIndex = htmlData.lastIndexOf("]");
if (htmlData.slice(lastIndex-3, lastIndex-2) == "." && htmlData.slice(lastIndex-6, lastIndex-5) == ":") {
var tmpBR = htmlData.slice(lastIndex);
htmlData = htmlData.slice(0, lastIndex+tmpBR.indexOf(" htmlData = htmlData.slice(0, htmlData.indexOf(" } else {
htmlData = htmlData.slice(0, lastIndex);
if (loopStart == undefined) {
loopCount = 1;
} else {
delete loopStart;
}
if (loopCount<254) {
loopStart = true;
loopCount++;
findEndTag();
}
}
}
function error() {
if (nextPageLink != undefined) {
if (linkIndex>linkData.length-2) {
myLoadVars = new LoadVars();
myLoadVars.load(nextPageLink);
myLoadVars.onData = getLink;
pageIndex++;
linkIndex = 1;
delete nextPageLink;
} else {
linkIndex++;
openLRCPage(linkData[linkIndex-1]);
}
}
}
我们先来看第一句:
System.useCodepage = true;
这句主要的作用是让最后的结果不会变成乱码,默认情况下flash是按照UTF格式来解析所有加载的数据的,这在加载中文数据的时候会有些问题,只有数据在用UTF-8编码格式的时候才能被正确识别。因为很多页面(基本上现在90%的页面)并不是用UTF-8来保存的,所以要加上这句。这样才不会乱码。更详细的说明请参见flash帮助关于System.useCodepage的详细说明。反正在大部份情况下,如果你遇到乱码的问题都可以用这个来解决,使用 System.useCodepage = true 或者 System.useCodepage = false 来切换。
第二句:
myLoadVars = new LoadVars();
这是用来装载所有加载的数据的,等会加载的网页的数据(html源文件)都装在这里。基本上,要进行数据传输(例如和服务器程序交换变量数据或者从服务器上加载文件)的进候都要用到 LoadVars()、XML的数据传输/加载方法或者XMLSocket 。LoadVars() 是最基础的方法。
function lrcSearch(keyWord) {
musicName = keyWord.slice(0, keyWord.indexOf(" "));
myLoadVars.load("http://www.baidu.com/s?wd="+"[ti:"+keyWord);
myLoadVars.onData = getLink;
pageIndex = 1;
}
这里定义了一个函数 lrcSearch(keyWord) 。呆会每次搜索不同歌词的时候都是从这里开始的,通过向这个函数发送不同的歌词名(如 lrcSearch("突然的自我")、 lrcSearch("童话")、lrcSearch("不得不爱")或者lrcSearch("我听这种音乐的时候最爱你"))来启动搜索。在这里,是所有代码的入口。
它告诉flash:加载(下载) "http://www.baidu.com/s?wd="+"[ti:"+keyWord 的文件。keyWord 是刚刚上面所说的 "突然的自我" 等需要搜索的关键字。 当你在搜索引擎搜索内容的时候注意一下地栏的url,在百度中,它是类似于这样:
www.baidu.com/s?wd=%5Bti%3A%CD%BB%C8%BB%B5%C4%D7%D4%CE%D2&cl=3
你可以更改里面的"%5Bti%3A%CD%BB%C8%BB%B5%C4%D7%D4%CE%D2&cl=3"来完成不同的搜索,这里是刚刚输入的“[ti:披着羊皮的狼”的对应URL标准格式编码,你可以在浏览器中将这段文本转换成“[ti:突然的自我”试试。它会帮你自动转换成相应的URL来进行以后的工作(下载网页)。在flash中,也是一样的。所以你可以通过加载 www.baidu.com/s?wd=[ti:突然的自我 这样的URL来获得在浏览器中打开的文件(它们的html源代码)。也就是上面的那句:
myLoadVars.load("http://www.baidu.com/s?wd="+"[ti:"+keyWord);
接着,myLoadVars.onData = getLink 定义了LoadVars的onData的事件。让 flash 在下载完网页后执行getLink这个函数的代码。
LoadVars有两个事件(也就是当某一件事完成后要怎么样):LoadVars.onData 和 LoadVars.onLoad 。在这里,我们使用 LoadVars.onData。它的写法如下:
my_lv.onData = function(src) {
// 此处是您的语句
}
它定义了当下载完成的时候该执行的代码。src 是加载的原始(未分析)数据(在这里是下载的网页的源代码)。它直接定义了一个函数。你也可以将这个函数写在其它地方或者调用其它你已经写好的函数。也就是这样写:
my_lv.onData = getLink 或者 my_lv.onData = getLink(src); (但注意不要这样写 my_lv.onData = getLink() ,因这样会丢失刚刚获得获得的网页源代码)
musicName = keyWord.slice(0, keyWord.indexOf(" ")); 这一句我就不详细论述了,只告诉你它用来完成搜索中使用空格隔开关键字的功能。使用更多的用空隔间隔的关键字可以更快更准确的搜索到所要的内容,如加入歌手 "披着羊皮的狼 谭咏麟" 的结果就可能比 "披着羊皮的狼" 更准确,还可以对歌词的内容作界定,这可以将同名歌曲区别开来,例如"青春"这首歌曲就有5个以上的版本,像这样 "青春 你我都不会变不会变"(在这里,歌手的名字则可有可无,大部份情况下,无更好一些)。pageIndex = 1 并不是很重要,它用来完善一些可用性的事情,也就不多说了。
下面转到 getLink 函数:
function getLink(src) {
nextPageLink = "http://www.baidu.com/"+src.slice(src.lastIndexOf("href=", src.indexOf("下一页"))+5, src.lastIndexOf("><", src.indexOf("下一页")));
linkData = src.split("快照");
for (var i = 0; i
linkData[i] = linkData[i].slice(0, linkData[i].lastIndexOf(" linkData[i] = linkData[i].slice(linkData[i].lastIndexOf("href=")+6); linkData[i] = linkData[i].slice(0, linkData[i].indexOf("\"")); } linkIndex = 1; openLRCPage(linkData[0]);}这是用来解析网页源文件的代码,我们用它来分析得到的搜索结果。第一句 nextPageLink = "http://www.baidu.com/"+src.slice(src.lastIndexOf("href=", src.indexOf("下一页"))+5, src.lastIndexOf("><", src.indexOf("下一页"))) 就不说了,它用来获得下一页的链接。linkData = src.split("快照") 它将网页的源代码以"快照"作为参照拆为不同的段,然后放到数组中。事实上,linkData=new Array(); linkData = src.split("快照") 这样写更标准一些。选取一个好的拆分关键字非常重要,它可以让你的运算量成倍降低(特别是要处理的数据量更大的时候,就更明显),在这里,我们使有"快照"这两个字,看看百度返回的结果:-----------------------------------------------------伍佰-突然的自我[ti:突然的自我][al:伍佰&china blue 200 ][offset:0][00:05.00] 突然的自我[00:12.87][00:31.74]听见你说朝阳起又落[00:37.62]晴雨难测道路是脚步多[00:43.49]我已习惯你突然间的自我[00:49.37]挥挥洒洒将自然看...www.musictea.com/music/lrc86407.htm?t=突 ... 4K 2005-12-24 - 百度快照www.musictea.com 上的更多结果水晶手机信息技术网--wav铃声 黄小琥 - 突然的..[ti:突然的自我][ar:黄小琥][al:][by:wolfpey][offset:500][00:02.67]《突然的自我》[00:10.12...[ti:突然的自我][ar:黄小琥][al:][by:wolfpey][offset:500][00:02.67]《突然的自我》[00:10.12...www.zjnetcafe.com/html/7/60586.htm 5K 2005-10-14 - 百度快照-----------------------------------------------------这里只显示了两段文本,它有十个这样的文本块。分析一下,尽管每块文本的标题和内容都不同,但是每一段都会出现 "百度快照 " 这样的内容。在 Google 中则是这个: "网页快照 - 类似网页"在百度中,你可以选择"百度快照"作为拆分的参照(如果查看源代码,也可以找到更多的参照),在 Google 中,"网页快照" 和 "类似网页" 都可以使用 ,在这里,我们以 Google 为实例,说明如何选取好的关键字的一个例子:(在百度中,这只是一个巧合,就不作为教程的参考了)在 Google 搜索框中输入"[ti:披着羊皮的狼",然后查看一下网页的源代码(菜单:查看> 源文件 或者用右键弹出菜单)。它们和你在 flash 中获得的数据是一样的。找到每个重复类似的文本块的那一段源代码:(我们取第二段)*********************************郑源/披着羊皮的狼.LRC 飞舞歌词fei5.com郑源/披着羊皮的狼.LRC. ... [ti:披着羊皮的狼] [ar:郑源] [al:我最火] [by:fei5.com] [offset:-500] [00:01.00]-披着羊皮的狼- [00:04.00]歌手:郑源[00:08.00]专辑:我最火[00:12.00]词曲:刀郎[00:16.00] ...www.rixiu.com/fei5/11/1140657ae45bc98.html - 6k - 补充材料 -网页快照 -类似网页*********************************看看"网页快照" 和 "类似网页" 的位置, "类似网页"差不多是在结尾了,直觉上,用 "类似网页" 分割将更不会产生混淆,更完整(因为如果用 "网页快照" 分割出来的数据,属于这一段在"网页快照" 的一部份数据将会分割到下一段中,一个分割段中包含了两块文本的数据而造成混乱)看看我们要取得什么,最终我们要得到的是指向最终页面的超链接。也就是这一段 " 郑源/披着羊皮的狼.LRC 飞舞歌词fei5.com" 事实上,我们只要取得这个超链接就可以了:www.rixiu.com/fei5/11/1140657ae45bc98.html最终我们将用String的slice方法取得这个字符串,但在取之前我们必须找到字符串开始的位置"http://" 和最后结束的位置 "bc98.html" ,这就要用到String.indexOf()或String.lastIndexOf()方法。在这里,我们使用逆向查找的方法。也就是String.lastIndexOf()。它是从后向前搜索,如果找到,它将返回靠后的那个字符串的位置。参数将指定要搜索的字符串,它还有一个搜索起始位置的参数(在比较高级查找的时候有用),在这里我们只用第一个字符串参数,详细请看看官方帮助文档中的说明:my_str.lastIndexOf(substring, [startIndex])参数:substring 一个整数或字符串,指定要搜索的字符串。startIndex 一个可选整数,指定搜索 substring 的起始点。返回:指定子字符串的最后一个匹配项的位置,或 -1。说明:方法;在字符串中从右向左搜索,并返回调用此方法的字符串中在 startIndex 之前找到的 substring 的最后一个匹配项的索引。如果没有找到 substring,则此方法返回 -1。for (var i = 0; i......}对每个区块执行同样的操作。现在我们只看看其中一块的处理。看看用 "网页快照" ("快照" )截断的方式:*********************************-
类似网页
郑源/披着羊皮的狼.LRC 飞舞歌词fei5.com
郑源/披着羊皮的狼.LRC. ... [ti:披着羊皮的狼] [ar:郑源] [al:我最
火] [by:fei5.com] [offset:-500] [00:01.00]-披着羊皮的狼- [00:04.00]歌手:郑
源[00:08.00]专辑:我最火[00:12.00]词曲:刀郎[00:16.00] ...
www.rixiu.com/fei5/11/1140657ae45bc98.html - 6k - 补充材料 -
网页快照
*********************************
下面的处理事实上就是一层层剥离数据的过程。
第一句 linkData[i] = linkData[i].slice(0, linkData[i].lastIndexOf("选取有特征的字符串在这里很重要。看看上面的这段代码,关于搜索结果的那一段("郑源/披着羊皮的狼.LRC......c98.html - 6k")都是不能作为特征进行数据搜索处理的,因为它们会随着搜索结果的不同而改变。所有可用的就是关于超链接的部份(搜索引擎不管如何更改他们的页面,点向最终页面的这个链接始终都是会存在的,随着时间的推移,在很长一段时间内也都是如此,这里有为你考虑到程序的寿命问题,也就是说,你写的这段程序在很长的一段时间内都是有用的)。也就是源代码中的""标签。在这里我们用了"linkData[i].lastIndexOf("- *********************************-类似网页郑源/披着羊皮的狼.LRC 飞舞歌词fei5.com*********************************对比一下上面用"类似网页"截断的数据,要处理到这一步,就需要重复处理两次。(因为 "第二句 linkData[i] = linkData[i].slice(linkData[i].lastIndexOf("href=")+6) 从后面查找 "href="(现在最后的一个超链接区域就是我们想要的链接了,这就是为什么我们用lastIndexOf从后面搜索)。不过稍微不同的是,现在取在"href="之后的字符串(上一步是之前)。经过这次处理剩下的代码如下:*********************************www.rixiu.com/fei5/11/1140657ae45bc98.html"; target=_blank onmousedown="return clk(0,'res','2','')">郑源/披着羊皮的狼.LRC 飞舞歌词fei5.com
*********************************
第三句 linkData[i] = linkData[i].slice(0, linkData[i].indexOf("\"")) 取得第一个引号以前的字符串。这里我讲一下为什么不用 target= 或者.html 作为特征处理的原因,上面的代码实际上也可以将 target= 放在后面,也不会影响最终页面的结果。像这样:
www.rixiu.com/fei5/11/1140657ae45bc98.html"; onmousedown="return clk(0,'res','2','')" target=_blank>郑源/披着羊皮的狼.LRC
飞舞歌词fei5.com
所以,它是不可识别和预知的。而.html 则并不是因为所有网页都是以.html作为结尾的,有些可能是.htm ,有些更可能是像?sl=11&word=aa这样动态生成的格式。(其实" 也并不是非常稳健的方式,因为有些不标准的网页可能将引号" 去除,这时候替代的方法是使用空格)
*********************************
www.rixiu.com/fei5/11/1140657ae45bc98.html
*********************************
现在最终我们得到了这个超链接。
通过上面的for (var i = 0; i
现在我们已经得到了搜索结果页面的每个通向最终页面的链接(Google 和百度都如此,教程中对两者的特征字作了兼容),在百度中,一般而言,只要打开第一个链接就可以了。准确性差不多高过95%。
openLRCPage(linkData[0])
linkIndex = 1 这句就不说了,用来处理一些逻辑。使它可以在第一个链接失效的时候也能继续运行。
function openLRCPage(url) {
myLoadVars.load(url);
myLoadVars.onData = getLRC;
}
这个和一开始加载搜索结果的代码很相似,只不过将处理的函数传给了 getLRC。
function getLRC(src) {
htmlData = src.slice(src.lastIndexOf("[ti:"+musicName));
findEndTag();
......
现在开始处理最终的含有LRC内容的网页了(从搜索引擎中打开)。最终的结果页可能如下:
(前面省略)
[ti:突然的自我]
[ar:黄小琥]
[al:]
[by:wolfpey]
[offset:500]
[00:02.67]《突然的自我》
[00:10.12]作词:伍佰 徐克
[00:11.67]作曲/编曲:伍佰
[00:12.30]演唱:黄小琥
(后面省略)
具体请从搜索引擎打开几个用"[ti:突然的自我"搜索的页面看看。现在我们直接取在我们想要的LRC内容开始后的网页代码。也就是以 "[ti:"+musicName) 为特征开始的网页代码。(为了便于观察,我这里以实际显示的内容为参考,并没有列出网页的源代码,但它们有时是可以相互通用的,在网页源代码都会包含最终显示的内容,某些特殊的转义字符除外。只是更为杂乱。插一句,有些网页的源代码确实够乱的,很可能让你的程序崩溃)
我们可以用indexOf()和lastIndexOf()查找。考虑到有些网页中(特别是论坛)会出现两次以上的歌词。也许是后一个发贴者贴了一个更准确的歌词或者其它情况(比如有人将它作为主题标头内容发表)。我们这里使用lastIndexOf(),我们从后面开始查找(我们假定后一个歌词比前一个歌词更准确)。并选取在它("[ti:突然的自我")之后的所有内容用作后面的处理。
既然我们已经找到了歌词的头,那么只要我们找到歌词的结尾并选取在这之间的内容差不多就算完成了。findEndTag() 函数用来查找歌词的结尾。
function findEndTag() {
var lastIndex = htmlData.lastIndexOf("]");
if (htmlData.slice(lastIndex-3, lastIndex-2) == "." && htmlData.slice(lastIndex-6, lastIndex-5) == ":") {
var tmpBR = htmlData.slice(lastIndex);
htmlData = htmlData.slice(0, lastIndex+tmpBR.indexOf("
htmlData = htmlData.slice(0, htmlData.indexOf("
} else {
htmlData = htmlData.slice(0, lastIndex);
if (loopStart == undefined) {
loopCount = 1;
} else {
delete loopStart;
}
if (loopCount<254) {
loopStart = true;
loopCount++;
findEndTag();
}
}
}
LRC歌词是有特征可以区别的。看看LRC文件,它每一行都包含了像"[00:30.11]"这样的时间标记,我们的目标就是找到它。首先我们查找 "]" (不能查找 "[00:30.11]" ,里面的时间是不确定的,flash 也不支持正则的方式)
var lastIndex = htmlData.lastIndexOf("]") 使用lastIndexOf()从后面向前查找的方式找到最后一个 "]" 字符。这里我们用到了一个 var 声明一个局部变量。如果你在函数过程中要使用一个临时性的东西,都最好先用var声明一下,它将在函数运行完后自动销毁,以便更节省内存(另外一个好处就是可以帮你防止和外面的名字冲突而引起不必要的麻烦)
但光有这些还不够,下一步就是要验证我们找到是不是像"[00:30.11]"这样的字符。要完成验证的另外一个原因就是可能网页中(特别是论坛贴子中)其它地方也可能出现"]"这样的字符。它们可能包含一大堆像"[3]:我想找...[4]:喜欢这歌,顶下"、"第[1]页"这样的包含"]"字符内容(甚至是像[ MP=480,360,false]http://music.f4666.com/music/turanziwo.mp3[/MP]"这样的论坛发贴的代码)。可以参考一下这个网页或其它论坛:
www.zjnetcafe.com/html/7/60586.htm
if (htmlData.slice(lastIndex-3, lastIndex-2) == "." && htmlData.slice(lastIndex-6, lastIndex-5) == ":") 对它进行验证,在这里我只验证两个字符:在它前面3个字符的"." 和 在它前面6个字符的":"。你可以加上验证在它前面9个字符的"["来加强验证。代码是:
if (htmlData.slice(lastIndex-3, lastIndex-2) == "." && htmlData.slice(lastIndex-6, lastIndex-5) == ":" && htmlData.slice(lastIndex-9, lastIndex-8) == "[")
如果验证失败(也就是说它不是我们想找的像"[00:30.11]"这样的标签),我们将再向前搜索直到找到这个标签为止,跳到else部份:
else {
htmlData = htmlData.slice(0, lastIndex);
......
findEndTag();
}
}
htmlData = htmlData.slice(0, lastIndex) 截取在找到的"]"这个字符之前的内容用作以后的处理,findEndTag() 使它继续重复上面查找验证的工作。
注意,findEndTag()运行了它自已身,我们的整个查找验证的函数实际上是一个自循环(自已在函数内部调用自已)。
flash中一个函数递归256次的限制。也就是一个函数不能重复执行256次。超过256次 flash 就将终止它并可能停止执行所有的ActionScript(接近于程序崩溃了)
这里我们自行加了一个计数器判断将执行次数限制在256次以内以免到时flashPlayer终止它让你的程序崩溃。
if (loopStart == undefined) {
loopCount = 1;
} else {
delete loopStart;
}
if (loopCount<254) {
loopStart = true;
loopCount++;
这里用了一个一个相互排斥的锁定的方法以使函数明白自已是不是第一次执行。如果是,则重新开始计数。如果不是,则将计数器累加,并将次数限制在规定次数以内。
自循环函数是比较危险的,如果不是必要,请尽量少使用它。(或者用两个AB相同的函数交叉调用代替,但也请注意别让函数陷在死循环里跳不出来,极度危险。更好的解决方案是将函数的执行放在onEnterFrame事件中,但可能会牺牲点执行速度,写法如下:)
function findEndTag() {
this.onEnterFrame=function(){
var lastIndex = htmlData.lastIndexOf("]");
if (htmlData.slice(lastIndex-3, lastIndex-2) == "." && htmlData.slice(lastIndex-6, lastIndex-5) == ":") {
var tmpBR = htmlData.slice(lastIndex);
htmlData = htmlData.slice(0, lastIndex+tmpBR.indexOf("
htmlData = htmlData.slice(0, htmlData.indexOf("
delete this.onEnterFrame;
doFunction();
} else {
htmlData = htmlData.slice(0, lastIndex);
}
}
}
我们回到验证成功后的部份:
var tmpBR = htmlData.slice(lastIndex);
htmlData = htmlData.slice(0, lastIndex+tmpBR.indexOf("
htmlData = htmlData.slice(0, htmlData.indexOf("
我们只过滤掉我们不想要的部份,注意,不是从"]"这个字符串的位置过滤,这样你将丢掉最后一行歌词的内容。我们查找最靠近这一行歌词的源代码标签(这里我们开始要用到源代码来识别了)"
因为有些歌词的旁边并没有"
用高级的查找方式(加个起始位置参数)你也可以将上面的代码直接写成这样:
htmlData = htmlData.slice(0, htmlData.indexOf("
htmlData = htmlData.slice(0, htmlData.indexOf("
最后我们回到这里:
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
lrcData = htmlData.split(String.fromCharCode(10)).join("");
看看最后我们得到的LRC歌词内容,它大致是下面的样子:
......
[ti:突然的自我]
[ar:黄小琥]
[al:]
[by:wolfpey]
[offset:500]
[00:02.67]《突然的自我》
[00:10.12]作词:伍佰 徐克
[00:11.67]作曲/编曲:伍佰
[00:12.30]演唱:黄小琥
......
最后我们将所有
标签去除并替换成换行符 "\n"(LRC分行的写法)。
(注:split().join()用来替换字符串,请参见wersling的站点:http://www.wersling.com/blog/ )
考虑到html和xhtml的兼容性,所以这里用了两次同样的方法。第一次对html常用的"
"标签进行替换,第二次对xhtml的"
"标签进行替换。
如果再考虑到大小写"
"的问题,应该再写成这样:
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
htmlData = htmlData.split("
").join("\n");
也可以用先转换大小写的方式(但请注意记得还原原来LRC内容的大小写),幸好这里还不算太长(只有br两个字母)
最后,去掉多余的换行符。(String.fromCharCode(10) 等同于 "\n" );
(考虑对特别内容的兼容性,最后一行代码其实还是有些问题的,一些站点可能并没有"
"标签而直接用换行符替代,这样最后一行代码会引发问题。)
如果再加上某些站点可能使用回车符换行的方式,应该替换成这样(共有4种组合方式):
回车+回车;回车+换行;换行+换行;换行+回车;
或者设计一个循环检查代码(针对三个以上连续回车换行的情况,这种情况极少)
顺便就推广测试一下实用性的东西吧: