用asp+xmlhttp编写web采集程序

时间:2021-08-30 06:25:35

web采集程序?网页抓取程序?小倫程序?不管怎么叫,这种程序应用倒是蛮广的。本文不讨论这种使用这种程序引起的版权或道德问题,只谈这种程序在ASP+VBScript环境下的实现 :-) 

预备知识:除了一般的ASP+VBScript的知识外,你还需要了解xmlhttp对象和正则表达式对象。xmlhttp对象是时下风头正劲的Ajax的主角;而学好了正则表达式,你再也不用为处理复杂的字符串犯愁。 

在编写和调试正则表达式时,RegEx 这个小工具非常有用。  

目录 
抓取一个远程网页并保存到本地  
改进:处理乱码  
同时下载远程网页的图片(和其它文件)  
改进:探测真实URL  
改进:避免重复下载  
实战举例(以****为例)  
分析列表页  
内容页的技巧  
分析内容页中的上一页,下一页  
高级主题:UTF-8和GB2312的转换  
更多高级主题:登陆后抓取,客户端伪造  
己有的采集程序  
原文链接:http://gwx.showus.net/blog/article.asp?id=229 


1.抓取一个远程网页并保存到本地 
'用于调试的过程,后面会多次调用检查中间结果 
Dim inDebug:inDebug=True  
Sub D(Str) 
    If inDebug = False Then Exit Sub 
    Response.Write("<div style='color:#003399; border: solid 1px #003399; background: #EEF7FF; margin: 1px; font-size: 12px; padding: 4px;'>") 
    Response.Write(Str &"</div>") 
    Response.Flush() 
End Sub 

'过程: Save2File 
'功能: 把文本或字节流保存为文件 
'参数: sContent    要保存的内容 
'       sFile       保存到文件,形如"files/abc.htm" 
'       bText       是否是文本 
'       bOverWrite  是否覆盖己存在文件 
Sub Save2File(sContent,sFile,bText,bOverWrite) 
    Call D("Save2File:"+sFile+" *是否文本:"&bText) 
    Dim SaveOption,TypeOption 
    If (bOverWrite = True) Then SaveOption=2 Else SaveOption=1 
    If (bText = True) Then TypeOption=2 Else TypeOption=1 
    Set Ads = Server.CreateObject("Adodb.Stream") 
    With Ads 
        .Type = TypeOption  
        .Open 
        If (bText = True) Then .WriteText sContent Else .Write sContent 
        .SaveToFile Server.MapPath(sFile),SaveOption 
        .Cancel() 
        .Close() 
    End With 
    Set Ads=nothing 
End Sub 

关键的函数 
'函数: myHttpGet 
'功能: 抓取一个远程文件(网页或图片等)并保存到本地 
'参数: sUrl    远程文件的URL 
'       bText   是否是文本(网页),下载远程图片是bText=False 
'返回: 抓取的内容 
Function myHttpGet(sUrl,bText) 
    Call D("<font color=red>myHttpGet:</font>"+sUrl+" *是否文本:"&bText) 
    'Set oXml = Server.CreateObject("Microsoft.XMLHTTP") 
    Set oXml = Server.CreateObject("MSXML2.ServerXMLHTTP")  '服务器版本的XMLHTTP组件 
    '理解下面的内容,你可以参考一下MSDN中的MSXML2.ServerXMLHTTP 
    With oXml 
        .Open "GET",sUrl,False 
        .Send 
        While .readyState <> 4  '等待下载完毕 
            .waitForResponse 1000  
        Wend  
        If bText = True Then 
            myHttpGet = bytes2BSTR(.responseBody) 
        Else 
            myHttpGet = .responseBody 
        End If 
    End With 
    Set oXml = Nothing 
End Function 

改进:处理乱码 
直接读取服务器返回的中文内容会出现乱码,myHttpGet函数中引用的bytes2BSTR的作用是正确读取服务器返回的文件中的双字节文本(比如说中文) 
'myHttpGet helper 处理双字节文本 
Function bytes2BSTR(vIn) 
    strReturn = "" 
    For i = 1 To LenB(vIn) 
        ThisCharCode = AscB(MidB(vIn,i,1)) 
        If ThisCharCode < &H80 Then 
            strReturn = strReturn & Chr(ThisCharCode) 
        Else 
            NextCharCode = AscB(MidB(vIn,i+1,1)) 
            strReturn = strReturn & Chr(CLng(ThisCharCode) * &H100 + CInt(NextCharCode)) 
            i = i + 1 
        End If 
    Next 
    bytes2BSTR = strReturn 
End Function 

bytes2BSTR函数的功能也可以利用Adodb.Stream组件通过下面的函数实现,虽然下面的函数可以指定字符集Charset,但它并不能转换编码,即传递"UTF-8"给参数sCset,来读取一张GB2312编码的网页将显示为乱码。 
'CharsetHelper可以正确的读取以sCset(如"GB2312","UTF-8"等)编码的文件 
Function CharsetHelper(arrBytes,sCset) 
    Call D("CharsetHelper: "+sCset) 
    Dim oAdos 
    Set oAdos = CreateObject("Adodb.Stream") 
    With oAdos 
        .Type = 1 
        .Mode =3    'adModeReadWrite 
        .Open 
        .Write arrBytes 
        .Position = 0 
        .Type = 2 
        .Charset = sCset 
        CharsetHelper = .ReadText  
        .Close 
    End With 
    Set oAdos = Nothing 
End Function  

2.同时下载远程网页的图片(和其它文件) 
'函数: ProcessRemoteUrl 
'功能: 替换字符串中的远程文件为本地文件并保存远程文件 
'参数: strContent  要替换的字符串,即远程网页文件的内容 
'       sSavePath   不以/结尾的相对路径,指示远程文件的本地保存路径 
'       sPreceding  更改后的URL前缀,如http://somehost/upload/ 
'返回: 替换远程路径为本地路径之后的新的网页文本内容 
Function ProcessRemoteUrl(sContent,sSavePath,sPreceding) 
    Call D("ProcessRemoteUrl") 

    Set re=new RegExp 
    re.IgnoreCase =true 
    re.Global=True 
    '下面的正则中.SubMatches(4)=文件名全名.SubMatches(5)文件扩展名 
    re.Pattern = "((http):(?:\/\/){1}(?:(?:\w)+[.])+(net|com|cn|org|cc|tv|[0-9]{1,4})(\S*\/)((?:\S)+[.]{1}(gif|jpg|jpeg|png|bmp)))" 

    Set RemoteFile = re.Execute(sContent) 

    Dim SaveFileName 
    'RemoteFile     正则表达式Match对象的集合 
    'RemoteFileUrl  正则表达式Match对象 
    For Each RemoteFileUrl in RemoteFile 
        SaveFileName = RemoteFileUrl.SubMatches(4) 
        Call Save2File(myHttpGet(RemoteFileUrl,False),sSavePath&"/"&SaveFileName,False,True) 
        sContent=Replace(sContent,RemoteFileUrl,sPreceding&SaveFileName) 
    Next 

    ProcessRemoteUrl=sContent 
End Function  
改进:探测真实URL 
上面的ProcessRemoteUrl函数不能正确处理形如<img src="upload/abc.jpg" />和<a href="/upload/abc.gif" ...的内容,要处理这些相对链接,我们可以先用下面的函数把网页中的相对链接都转换成绝对链接 
'函数: DetectUrl 
'功能: 替换字符串中的远程文件相对路径为以http://..开头的绝对路径 
'参数: sContent    要处理的含相对路径的网页的文本内容 
'       sUrl        所处理的远程网页自身的URL,用于分析相对路径 
'返回: 替换相对链接为绝对链接之后的新的网页文本内容 
Function DetectUrl(sContent,sUrl) 
    Call D("DetectUrl:"&sUrl) 

    '分析URL 
    Dim re,sMatch 
    Set re=new RegExp 
    re.Multiline=True 
    re.IgnoreCase =true 
    re.Global=True 

    re.Pattern = "(http://[-A-Z0-9.]+)/[-A-Z0-9+&@#%~_|!:,.;/]+/" 
    Dim sHost,sPath 
    'http://localhost/get/sample.asp 
    Set sMatch=re.Execute(sUrl) 
    'http://localhost 
    sHost=sMatch(0).SubMatches(0) 
    'http://localhost/get/ 
    sPath=sMatch(0) 

    re.Pattern = "(src|href)=""?((?!http://)[-A-Z0-9+&@#%=~_|!:,.;/]+)""?" 
    Set RemoteFile = re.Execute(sContent) 

    'RemoteFile 正则表达式Match对象的集合 
    'RemoteFileUrl 正则表达式Match对象,形如src="Upload/a.jpg" 
    Dim sAbsoluteUrl 
    For Each RemoteFileUrl in RemoteFile 
        '<img src="a.jpg">,<img src="f/a.jpg">,<img src="/ff/a.jpg"> 
        If Left(RemoteFileUrl.SubMatches(1),1)="/" Then 
            sAbsoluteUrl=sHost 
        Else 
            sAbsoluteUrl=sPath 
        End If 
        sAbsoluteUrl = RemoteFileUrl.SubMatches(0)&"="""&sAbsoluteUrl&RemoteFileUrl.SubMatches(1)&"""" 
        sContent=Replace(sContent,RemoteFileUrl,sAbsoluteUrl) 
    Next 

    DetectUrl=sContent 
End Function  
改进:避免重复下载 
网页中的有些图片,比如spacer.gif重复出现,会被重复下载,壁免这个问题的一个方法是设置一个arrUrls数组,把采集过的文件的URL放在里面,在每次采集前先遍历数组看是否已经采集,然后只参集没有参集过的文件 

3.实战举例(以****为例) 
****是我最经常去的地方,而且网速不错,就以她为例啦,没有恶意哦:-) 

分析列表页 
内容页的技巧 
分析内容页中的上一页,下一页 
想了一下,这部分内容还是晢时不写,免得被BS了  :-),还省得打好多字。 无非是把远程网页采集下来,然后用正则表达式分析提取其中的特定内容,如标题,作者,内容之类的 我有两个小小的经验:  

一是网页源码前后的内容对分析有很大的干扰,你可以用下面的方法先把它支除 
'抽取部分内容进行分析,你可以用用EditPlus数字数 
'去除前7600和后5000的字符 
sPageW=Left(sPageW,Len(sPageW)-5000) 
sPageW=Mid(sPageW,7600) 

二是你可能不想在对方的服务器上留下连续的浏览记录,下面的一个小函数会有所帮助 
'过程: Sleep 
'功能: 程序在此晢停几秒 
'参数: iSeconds    要暂停的秒数 
Sub Sleep(iSeconds) 
    D Timer()&" <font color=blue>Sleep For "&iSeconds&" Seconds</font>" 
    Dim t:t=Timer() 
    While(Timer()<t+iSeconds) 
        'Do Nothing 
    Wend 
    D Timer()&" <font color=blue>Sleep For "&iSeconds&" Seconds OK</font>" 
End Sub 

'调用举例,晢停,时长随机,在3秒以内 
Sleep(Fix(Rnd()*3))  

三就是多用正则表达式测试工具提高编写正则表达式的效率 

4.高级主题:UTF-8和GB2312的转换 
这个问题比较复杂,由于我智力和精力方面的原因没有完全搞定,网上己有的资料也大多不完全正确或者不全面,我推荐一个UTF-8和GB2312的转换的C语言的实现供大家参考,它功能完整而且不依赖Windows API函数。 
我在试着用ASP+VBScript实现它,有一些不太成熟的经验: 

计算机上的文件、操作系统内部的字符串表示都是Unicode的,所以,UTF-8和GB2312之间的转换需要以Unicode为中介  
UTF-8就是Unicode的一个变体,它们之间的相互转换比较简单,参考下图就可以了  
GB2312和Unicode的编码好像是不相关的,不依赖操作系统内部函数进行转换就需要一个编码映射表,指出GB2312和Unicode的编码一一对应的关系,这个编码表大约包含7480×2个项目。  
在ASP文件中,要默认以某和编码(如GB2312)读取一个字符串,需要将ASP的CodePage设为相应代码页(对GB2312是CodePage=936)  
编码转换中还有一些又小又重要的问题我还不知道:-(  
5.更多高级主题:登陆后抓取,客户端伪造等 
xmlhttp对象可以以post或get的方法与http服务器交互,可以设置和读取http头,学习一下http协议,并且更深入的了解一些xmlhttp对象的方法和属性,你就可以用它来模拟一个浏览器,自动的做各种以前需要人来做的重复工作。 


6.己有的采集程序 
本文旨在讨论采集程序在ASP+VBScript环境下的实现,如果你需要一个网页采集程序,下面的链接可能对你有用。  

LocoySpider火车头网页内容采集器  
C#+.Net编写的内容采集器,它的一个重要特点是不将采集来的内容保存到数据库,而是使用自定的POST提交的别的网页,如内容管理系统的新建内容页。免费。 BeeCollector (小蜜蜂采集器)  
PHP+MySQL编写的内容采集器。 风讯内容管理系统  
这个强大的内容管理系统内带有一个ASP的网页内容采集器+查看评论 (0)+发表评论+Trackback地址+Trackbacks (0)2006-8-9正则表达式在网络编程中的运用 
分类:Ajax时间:2006-8-9 14:07:47作者:janyin导读:  
正则表达式在网络编程中的运用 

[前言:]在我们编写WEB程序时,经常会判断一个字符串的有效性,如;一个串是否是数字、是否是有效的Email地址等等。如果不使用正则表达式,那么判断的程序会很长,并且容易出错,如果使用正则表达式,这些判断就是一件很轻松的工作了。本文全面介绍正则表达式的慨念、格式。并以在PHP、ASP中的应用实例增加读者的感性认识。正则表达式的应用很广,需要大家在学习和实践中不断的总结。 

正则表达式简介  
简单的说,正则表达式是一种可以用于模式匹配和替换的强有力的工具。在网络编程中应用广泛,如PHP脚本语言或是JavaScript、VBScript这样的客户端脚本语言都提供了对正则表达式的支持。由此可见,正则表达式已经超出了某种语言或某个系统的局限,成为人们广为接受的概念和功能。  

正则表达式可以让用户通过使用一系列的特殊字符构建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据比较对象中是否包含匹配模式,执行相应的程序。  

举例来说,正则表达式的一个最为普遍的应用就是用于验证用户在线输入的邮件地址的格式是否正确,如果通过正则表达式验证用户邮件地址的格式正确,用户所填写的表单信息将会被正常处理;反之,如果用户输入的邮件地址与正则表达的模式不匹配,将会弹出提示信息,要求用户重新输入正确的邮件地址。由此可见正则表达式在WEB应用的逻辑判断中具有举足轻重的作用。在后面我们会举例详细介绍。  

正则表达式形式一般如:/love/,其中位于"/"定界符之间的部分就是将要在目标对象中进行匹配的模式。用户只要把希望查找匹配对象的模式内容放入"/"定界符之间即可。为了能够使用户更加灵活的定制模式内容,正则表达式提供了专门的"元字符"。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。较为常用的元字符包括: "+,"*,?以及{}",或者"\s,\S,\d,\w和\W"等等。为了能够方便用户更加灵活的设定匹配模式,正则表达式允许使用者在匹配模式中利用[]界定匹配于某一个范围的字符而不局限于具体的字符。  

除了我们以上的元字符之外,正则表达式中还具有另外一种较为独特的专用字符,即定位符。定位符用于规定匹配模式在目标对象中的出现位置。较为常用的定位符包括: "^", "$", "\b" 以及 "\B"。  

如果我们希望在正则表达式中实现类似编程逻辑中的"或"运算,在多个不同的模式中任选一个进行匹配的话,可以使用管道符 "|"。例如: 正则表达式中还有一个较为常用的运算符,即否定符 "[^]"。与我们前文所提到的定位符 "^" 不同,否定符 "[^]"规定目标对象中不能存在模式中所规定的字符串。一般来说,当"^"出现在 "[]"内时就被视做否定运算符;而当"^"位于"[]"之外,或没有"[]"时,则应当被视做定位符。  

最后,当用户需要在正则表达式的模式中加入元字符,并查找其匹配对象时,可以使用转义符"\"。例如:/Th\*/,该正则表达式将会与目标对象中的"Th*"而非"The"等相匹配。 

正则表达式的语法规则和标记 
现在我们正式进入则表达式的学习,我会根据实例结合讲解正则表达式的用法,看完后你就会觉得写UBB代码如此简单了,只要你一步一步的跟着我学 看完本文章后你就成为UBB高手了。激动人心的就是你能写出自已的UBB标签来了,再也不用到别人那里去拷贝现成的代码和模板了。 还好VBScritp5.0给我们提供了"正则表达式"对象,只要你的服务器安装了IE5.x,就可以运行了. 

字符描述: 

^符号匹配字符串的开头。例如: 
^abc 与"abc xyz"匹配,而不与"xyz abc"匹配 

$符号匹配字符串的结尾。例如: 
abc$ 与"xyz abc"匹配,而不与"abc xyz"匹配。 

注意:如果同时使用^符号和$符号,将进行精确匹配。例如: 
^abc$ 只与"abc"匹配  

*符号匹配0个或多个前面的字符。例如: 
ab* 可以匹配"ab"、"abb"、"abbb"等 

+符号匹配至少一个前面的字符。例如: 
ab+ 可以匹配"abb"、"abbb"等,但不匹配"ab"。  

?符号匹配0个或1个前面的字符。例如: 
ab?c? 可以且只能匹配"abc"、"abbc"、"abcc"和"abbcc" 

.符号匹配除换行符以外的任何字符。例如: 
(.)+ 匹配除换行符以外的所有字符串 

x|y匹配"x"或"y"。例如: 
abc|xyz 可匹配 "abc"或 "xyz",而"ab(c|x)yz"匹配 "abcyz"和"abxyz" 

{n}匹配恰好n次(n为非负整数)前面的字符。例如: 
a{2} 可以匹配"aa",但不匹配"a" 

{n,}匹配至少n次(n为非负整数)前面的字符。例如: 
a{3,} 匹配"aaa"、"aaaa"等,但不匹配"a"和"aa"。 

注意:a{1,}等价于a+ 
a{0,}等价于a* 

{m,n}匹配至少m个,至多n个前面的字符。例如: 
a{1,3} 只匹配"a"、"aa"和"aaa"。 

注意:a{0,1}等价于a? 

[xyz]表示一个字符集,匹配括号中字符的其中之一。例如: 
[abc] 匹配"a"、"b"和"c" 

[^xyz]表示一个否定的字符集。匹配不在此括号中的任何字符。例如: 
[^abc] 可以匹配除"a"、"b"和"c"之外的任何字符 

[a-z]表示某个范围内的字符,匹配指定区间内的任何字符。例如: 
[a-z] 匹配从"a"到"z"之间的任何一个小写字母字符 

[^m-n]表示某个范围之外的字符,匹配不在指定范围内的字符。例如: 
[m-n] 匹配除从"m"到"n"之间的任何字符 

\符号是转义操作符。例如: 
\n 换行符 
\f 分页符 
\r 回车 
\t 制表符 
\v 垂直制表符  

\\ 匹配"\" 
\/ 匹配"/" 
\s 任何白字符,包括空格、制表符、分页符等。等价于"[ \f\n\r\t\v]" 
\S 任何非空白的字符。等价于"^\f\n\r\t\v]" 
\w 任何单词字符,包括字母和下划线。等价于"[A-Za-z0-9_]" 

\W 任何非单词字符。等价于"[^A-Za-z0-9_]" 

\b匹配单词的结尾。例如: 
ve\b 匹配单词"love"等,但不匹配"very"、"even"等 

\B匹配单词的开头。例如: 
ve\B 匹配单词"very"等,但不匹配"love"等 

\d匹配一个数字字符,等价于[0-9]。例如: 
abc\dxyz 匹配"abc2xyz"、"abc4xyz"等, 
但不匹配"abcaxyz"、"abc-xyz"等 

\D匹配一个非数字字符,等价于[^0-9]。例如: 
abc\Dxyz 匹配"abcaxyz"、"abc-xyz"等, 
但不匹配"abc2xyz"、"abc4xyz"等 

\NUM匹配NUM个(其中NUM为一个正整数),引用回到记住的匹配。例如: 
(.)\1 匹配两个连续相同的字符。  

\oNUM匹配n(其中n为一个小于256的八进制换码值)。例如: 
\o011 匹配制表符 

\xNUM匹配NUM(其中NUM为一个小于256的十六进制换码值)。例如: 
\x41 匹配字符"A" 

应用实例  
在对正则表达式有了较为全面的了解之后,就可以在Perl,PHP,以及ASP等程式中使用正则表达式了。 

下面以PHP语言为例,使用验证用户在线输入的邮件地址以及网址的格式是否正确。PHP 提供了eregi()或ereg()资料处理函数实现字串比对剖析的模式匹配操作ereg()函数的使用格式如下:  

ereg (pattern, string)  

其中,pattern代表正则表达式的模式;而string则是执行查找替换操作的目标对象,如Email地址值。本函式以 pattern 的规则来剖析比对字串 string,找到则传回值为 true。函式ereg()与eregi()的区别就是前者区分大小写,后者与大小写无关。使用PHP编写的程序代码如下:  

以下是引用片段: 
<?php   
if (ereg("^([a-z0-9_-])+@([a-zZ0-9_-])+(\.[a-z0-9_-])+[a-z]{2,3}$",$email))   
{ echo "您的 E-Mail 通过初步检查!";}   
else   
{ echo "不是合法的E-Mail 地址,请重新输入!";}   
?>   



这个例子是可对使用者输入的 E-Mail 作简单的检查,检查使用者的 E-Mail 字串是否有 @ 字元,在 @ 字元前有小写英文字母、数字或下"_",在 @ 之后有数节字串,最后的小数点后只能有二个或三个小写英文字母。如webmaster@mail.sever.net, hello_2001@88new.cn就可以通过检查,而New99@253.com(出现大写字母)和new99@253.comn(最后的小数点后只能超过3个英文字母)就不能通过检查。  

我们通过调用自定义正规则判别函式也可以进行检查操作,如下面的网址检验函式:  

以下是引用片段: 
function VerifyWebSiteAddr ($strWebSiteAddr){   
return (eregi ("^([_0-9a-z-]+.)+([0-9a-z-]+.)+[a-z]{2,3}$", $strWebSiteAddr));   
}   


我们知道,PHP程式的运行必须有服务器支持,如果您在自己的主页上想实现以上功能, 嵌入式脚本语言Javascript或许是好的选择。JavaScript中带有一个功能强大的RegExp()对象,可以用来进行正则表达式的匹配操作。其中的test()方法可以检验目标对象中是否包含匹配模式,并相应的返回true或false。只须在HTML文档的<head>区域添加一段Javascript代码。  

以下是引用片段: 
< language="Javascript1.2">   
function verifyAddress(obj){   
var email = obj.email.value;   
var pattern = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/;   
flag = pattern.test(email);   
if(flag){   
alert("您的 E-Mail 通过初步检查!");   
return true;}   
else{   
alert("不是合法的E-Mail 地址,请重新输入!");   
return false;}   
}   
</script>  


然后在网页中输入信息的表单域<form>标签区域内中加入一行如下代码:  

以下是引用片段: 
<onSubmit="return verifyAddress(this);">  


 当按下提交按钮后,首先运行verifyAddress()函式,进行匹配识别,如果满足条件则发送表单信息到目标页面,否则返回错误信息。  

事实上,正则表达式的功能远非本文提到的这一点,下次,给大家介绍一种使用正则表达式从任意指定网页中析取任意种类文本信息(如网页中所有的图片文件名)的技巧。  

Html源文件中image标签的析取  
上篇,我们介绍了正则表达式的概念及其在网络编程中使用正则表达式验证用户在线输入的邮件地址以及网址的格式是否正确的应用实例,今天介绍一种从指定网页源文件中析取image标签的的编程技巧,即从网页源文件中,解析出所有的插图文件名(包括图片路径),也就是标签<img src=".../.../abc.jpg"> 中的文件名".../.../abc.jpg"(有的可能是gif格式)。编程环境:PHP+Apache for Win98。  

首先, 用文本编辑器新建一个PHP类型的文件:abstractSRCfrompage.php3。为了方便讲解,我们打算是在浏览器表单域中输入需要析取image标签的网页的URL(或本机文档),提交后执行析取操作,所以在该文件中,我们要建立一个用于输入网址的表单,举例如下:  

以下是引用片段: 
<form action=" abstractSRCfrompage.php3" method="post">  
输入网址<input type=text name=filename><br>  
<input type=submit name=submit value="提交">  
</form>  


输入正确的网址,提交后表单信息被送到abstractSRCfrompage.php3页面,由于表单本身就在该页面,所以相当于被送到自身页面,下面我们需要编写析取处理的PHP代码,紧接着表单代码段后写入如下代码: 

以下是引用片段: 
<?php  
if ($filename!=""){  
$fp = fopen($filename, "r"); file://若输入不为空,开启本地或者远程档案;  
while ($buffer = fgets($fp, 1024)) {  
$source .=$buffer; }  
fclose($fp);  
file://查找$source中是否有<img ...src=".../...gif | jpg"> 这样的标记  
if(eregi("(<img)+[^<>]+(src=\")+[^\*\"<>|]+(\.)+((gif)|(jpg))+(\")",$source)) {  
echo "找到图片标签:)<br>";}  
else{  
echo "未发现图片标签:(<br>";}  
file://拆分,第一次用标签,<img ...src=拆分,得到了以图形文件名开头的数组,  
$splitres=split("((\">)|())+(<img)+[^<>]+(src=\")",$source);  
echo "找到: $imagenums-1个图片<br>分别为:<br>";  
for($i=1;$i<sizeof($splitres);$i++){  
file://二次拆分,用"拆分。因为文件名能含有",得到的拆分数组的第一个元素就是路径+文件名了;  
unset($imgname); // 再次使用前删除imgname变量;  
$imgname=spliti("\"",$splitres[$i]);//将析取的图片信息依次赋给imgname变量  
echo "$i=>".$imgname[0]."<br>"; file://输出析取的图片信息  
}  
}  
?>  


该段程序的设计思路是:PHP程式判断是否输入了档案名称(网址URL或本机档案名),若不为空则以只读方式打开该档案;接着使用函式fgets(fp,length)取得档案指标fp所指的行并传回该行内长度为length-1 的字串,上例中就是1024-1=1023;然后利用字串比对剖析函式ereg()查找$source中是否含有<img ...src=".../...gif | jpg">  

这样的标记(关于该函式在上篇中有详细的介绍);假如找到的话,则利用split()函式按一定的规则执行两次拆分,去掉标记中的<img...src=字符和"字符,结果得到数组splitres,其中的每一个元素都是以图形路径+文件名开头的数组;用for循环在屏幕上输出每个数组的第一个元素值,即我们所需要的所有图形路径+文件名。 

其中函式sizeof($splitres)返回个数;在for循环中,依次将数组splitres的每个元素(也是数组)赋给数组变量imgname,并输出imgname的第一个元素值(为找到的一个图形路径+文件名),当执行下次循环时,删除变量imgname,达到重复使用的目的。大家可以仔细研究其中的奥妙。 

好了,写好后,将abstractSRCfrompage.php3存到你的服务器指定目录下,启动Apache服务器,在浏览器中打开它,随便输入一个存在的网页名称或是远程URL,看看效果如何。 

如果有兴趣,你可以尝试析取HTML文档中的任意感兴趣的信息,如果稍加改装,做一个网站文本搜索引擎岂不更妙? 

正则表达式在UBB论坛中的应用 
一、UBB代码的概念 

什么是UBB代码? 

UBB代码是HTML的一个变种。一般情况下,UBB论坛不允许你使用HTML代码,而只能用UBB代码替代HTML代码。 

UBB代码是一套由流行的UBB标签组成了固定代码,代码有统一的格式。用户只要遵循代码规则就可以实现用户想要的功能。如:想要显示粗体的how are you 字样,就应该输入 how are you而不是输入<b>how are you</b> 

你也许会问:ASP是怎样把 how are you转换为<b>how are you</b>的呢? 

回答这个问题就是:用正则表达式。 

二、实例分析 

1)在字符串中精确查找链接地址  

以下是引用片段: 
((http|https|ftp):(\/\/|\\\\)((\w)+[.]){1,}(net|com|cn|org|cc|tv|[0-9]{1,3})(((\/[\~]*|\\[\~]*)  
(\w)+)|[.](\w)+)*(((([?](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*)  


我们知道,链接地址一般以http或者https或者ftp等形式出现。初步总结一下就是,链接地址必须符合如下条件: 

条件1 

以http://或者https://或者ftp://等开头(当然还有其它形式,这里只列出主要的) 

条件2 

http://后面必须跟一个单词字符,紧接着单词字符后面的是"."(这样的组合必须出现一次或多次)。紧跟着"."后面的是域名后缀(如net或者com或者cn等,如果是以IP地址的形式出现就可以是数字) 

条件3 

出现完整的链接地址后,还可以出现下一级或者更多级的目录(还要注意个人主页的地址有可能出现"~"符号) 

条件4 

链接地址末尾可以带参数。如典型的页数?PageNo=2&action=display等 

现在我们用下面的代码来逐个匹配上面的条件-- 

1、((http|https|ftp):(\/\/|\\\\) 满足条件1 

表示http:// http:\\ https:// https:\\ ftp:// ftp:\\都匹配(在这里考虑了某些用户可能把"//"输成"\\"的易发性错误) 

注意:"|"表示"或者","\"是转义字符。"\/\/"表示"//","\\\\"表示"\\" 

2、((\w)+[.]){1,}(net|com|cn|org|cc|tv|[0-9]{1,3}) 满足条件2 

"((\w)+[.]){1,}"表示一个单词字符加一个点号可以出现1次或者多次(这里考虑了某些用户喜欢省略www而将http://www.w3c.com写成http://w3c.com) 

"(net|com|cn|org|cc|tv|[0-9]{1,3})"表示必须要以net或者com或者cn或者org或者cc或者tv或者三位以下的数字结束 

[0-9]{1,3}表示三位以下的数字,因为ip地址的任何段不能超过255 

3、(((\/[\~]*|\\[\~]*)(\w)+)|[.](\w)+)* 满足条件3 

"(\/[\~]*|\\[\~]*)"表示可以出现"/~"或者是"\~",(其中"[\~]*"表示 ~ 可以出现也可以不出现),因为不是每个链接地址都有下一级目录 

"(\w)+)|[.](\w)+)"表示必须出现一个单词字符(即目录或者是一个带有扩展名的文件) 

注意:最后还有一个"*"表示上面括号内的可以出现也可以不出现,否则就只能匹配有下一级目录的链接地址了。 

4、(((([?](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*)满足条件4 

"((([?](\w)+){1}[=]*))*((\w)+){1}"表示形如"?PageNo=2"的字符串可以出现也可以不出现,如果出现则只能出现一次(因为不可能有两个"?"号出现)。 

"([\&](\w)+[\=](\w)+)*)"表示形如"&action=display"的字符串可以出现也可以不出现(因为并不是每个网页都带有两个以上的参数。 

整个"((([?](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*"表示形如"?PageNo=2&action=display"的字符串可以出现也可以不出现(即链接地址可以有参数也可以没有参数) 

把上面的组合起来,我们就可以匹配一个比较全面的链接地址了。比用简单的"(http:\/\/\S+)"来匹配一个链接地址要好,读者可以自行行测试比较。当然,这段代码还有很多不足之处,希望大家能够继续改进。 

2)替代典型的UBB标签: 

我们的目的就是要把成对的替换成<b></b>下面来看我们实现它的模板 

(\[b\])(.+)(\[\/b\]) 

这里用了"(.+)"来配匹到之间的整个字符串,在替代的时候我们要写成这样 

str=checkexp(re,str,"<b>$2</b>") 

(注意:checkexp是我自定义的函数,将在后面给出。这个函数将把按照我们提供的模板进行替代。) 

也许你会问这里出现一个"$2"是什么东东,呵注意了这个$2可是很重要的,它代表了"(.+)"所配匹的整个字符串。 

为什么是$2而不是$1、$3呢?因为$1代表(\[b\])所匹配的""字符串,$3代表(\[\/b\])所匹配的""字符串,显然这里我们需要的是$2而不 

是$1$3。 


三、UBB正则表达模板实例 

下面是我写的一个UBB函数,这个函数基本上能使你的论坛成为一个优秀的UBB代码论坛了。当然,通过改进后,你可以得到一个更强大的U 

BB论坛。 

代码片段: 
Function ReThestr(face,str)  
dim re,str  
re="\>"  
str=checkexp(re,str,">")  
re="\<"  
str=checkexp(re,str,"<")  
re="\n\r\n/"  
str=checkexp(re,str,"<P>")  
re=chr(32)  
str=checkexp(re,str," ")   
re="\r"  
str=checkexp(re,str," ")  
re="\[img\]((http:(\/\/|\\\\)){1}((\w)+[.]){1,3}_  
(net|com|cn|org|cc|tv)(((\/[\~]*|\\[\~]*)  
(\w)+)|[.](\w)+)*(\w)+[.]{1}(gif|jpg|png))\[\/img\]" ''查找图片地址  
str=checkexp(re,str," <img src=''$1''> ")  
re="\[w\](http:(\/\/|\\\\)((\w)+[.]){1,}_  
(net|com|cn|org|cc|tv)(((\/[\~]*|\\[\~]*)(\w)+)|[.](\w)+)*  
(((([?](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*)\[\/w\]" ''查找帧地址  
str=checkexp(re,str,"<iframe width=''300'' height=''300'' src=''$1''></iframe>")  
re="([^(''>)])(<br>)*((http|https|ftp):_  
(\/\/|\\\\)((\w)+[.]){1,}(net|com|cn|org|cc|tv|_  
([0-9]{1,3}))(((\/[\~]*|\\[\~]*)(\w)+)|[.](\w)+)*_  
(((([?](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*)" ''查找链接地址  
str=checkexp(re,str,"$1$2 <a href=''$3'' target=_blank>$3</a> ")  
re="([^(http://|http:\\)])((www|cn)[.](\w)+[.]{1,}_  
(net|com|cn|org|cc)(((\/[\~]*|\\[\~]*)(\w)+)|[.](\w)+)*  
(((([?](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*)  
" ''查找不以http://开头的地址  
str=checkexp(re,str,"$1 <a href=''http://$2'' target=_blank>$2</a> ")  
re="([^(=)])((\w)+[@]{1}((\w)+[.]){1,3}(\w)+)" ''查找邮件地址  
str=checkexp(re,str," <a href=''mailto:$2''>$2</a> ")  
re="\[color=(((\w)+)|][#][0-F]{6})\]((.)+)\[\/color\]" ''替换字体色彩  
str=checkexp(re,str,"<font color=''$1''>$4</font>")  
re="\[size=(][0-9]{1})\]((.)+)\[\/size\]" ''替换字体大小  
str=checkexp(re,str,"<font size=''$1''>$2</font>")  
re="\[font=((.)+){1,3}\]((.)+)\[\/font\]" ''替换字体  
str=checkexp(re,str,"<font face=''$1''>$3</font>")  
re="(\[b\])(.+)(\[\/b\])" ''加粗字体  
str=checkexp(re,str,"<b>$2</b>")  
re="(\[u\])(.+)(\[\/u\])" ''下画线  
str=checkexp(re,str,"<u>$2</u>")  
re="(\[li\])(.+)(\[\/li\])" ''列表  
str=checkexp(re,str,"<li>$2</li>")  
re="(\[QUOTE\])(.+)(\[\/QUOTE\])" ''引用  
str=checkexp(re,str,"_  
<BLOCKQUOTE>引用:<HR SIZE=1>$2<HR SIZE=1></BLOCKQUOTE>")  
re="\[@]{1}((\w)+[.]){1,3}(\w)+)\](.+)(\[\/email\])" ''邮件  
str=checkexp(re,str,"<a href=mailto:$1>$6</a>")  
re="(\[center\])(.+)(\[\/center\])" ''居中  
str=checkexp(re,str,"<center>$2</center>")  
re="fuck"  
str=checkexp(re,str,"***")  
re="操"  
str=checkexp(re,str,"***")  
re="sex"  
str=checkexp(re,str,"***")   
re="TMD"  
str=checkexp(re,str,"***")  
re="shit"  
str=checkexp(re,str,"***")  
ReThestr=str  
end function