用asp+xmlhttp编写web采集程序
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("
Response.Write(Str &"
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("myHttpGet:"+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函数不能正确处理形如和
'功能: 替换字符串中的远程文件相对路径为以http://..开头的绝对路径
'参数: sContent 要处理的含相对路径的网页的文本内容
' sUrl 所处理的远程网页自身的URL,用于分析相对路径
Function DetectUrl(sContent,sUrl)
re.Pattern = "(http://[-A-Z0-9.]+)/[-A-Z0-9+&@#%~_|!:,.;/]+/"
'http://localhost/get/sample.asp
re.Pattern = "(src|href)=""?((?!http://)[-A-Z0-9+&@#%=~_|!:,.;/]+)""?"
Set RemoteFile = re.Execute(sContent)
'RemoteFileUrl 正则表达式Match对象,形如src="Upload/a.jpg"
For Each RemoteFileUrl in RemoteFile
If Left(RemoteFileUrl.SubMatches(1),1)="/" Then
sAbsoluteUrl = RemoteFileUrl.SubMatches(0)&"="""&sAbsoluteUrl&RemoteFileUrl.SubMatches(1)&""""
sContent=Replace(sContent,RemoteFileUrl,sAbsoluteUrl)
****是我最经常去的地方,而且网速不错,就以她为例啦,没有恶意哦:-)
想了一下,这部分内容还是晢时不写,免得被BS了 :-),还省得打好多字。 无非是把远程网页采集下来,然后用正则表达式分析提取其中的特定内容,如标题,作者,内容之类的 我有两个小小的经验:
一是网页源码前后的内容对分析有很大的干扰,你可以用下面的方法先把它支除
sPageW=Left(sPageW,Len(sPageW)-5000)
二是你可能不想在对方的服务器上留下连续的浏览记录,下面的一个小函数会有所帮助
D Timer()&" Sleep For "&iSeconds&" Seconds"
D Timer()&" Sleep For "&iSeconds&" Seconds OK"
我在试着用ASP+VBScript实现它,有一些不太成熟的经验:
计算机上的文件、操作系统内部的字符串表示都是Unicode的,所以,UTF-8和GB2312之间的转换需要以Unicode为中介
UTF-8就是Unicode的一个变体,它们之间的相互转换比较简单,参考下图就可以了
GB2312和Unicode的编码好像是不相关的,不依赖操作系统内部函数进行转换就需要一个编码映射表,指出GB2312和Unicode的编码一一对应的关系,这个编码表大约包含7480×2个项目。
在ASP文件中,要默认以某和编码(如GB2312)读取一个字符串,需要将ASP的CodePage设为相应代码页(对GB2312是CodePage=936)
本文旨在讨论采集程序在ASP+VBScript环境下的实现,如果你需要一个网页采集程序,下面的链接可能对你有用。
C#+.Net编写的内容采集器,它的一个重要特点是不将采集来的内容保存到数据库,而是使用自定的POST提交的别的网页,如内容管理系统的新建内容页。免费。 BeeCollector (小蜜蜂采集器)
这个强大的内容管理系统内带有一个ASP的网页内容采集器+查看评论 (0)+发表评论+Trackback地址+Trackbacks (0)2006-8-9正则表达式在网络编程中的运用
分类:Ajax时间:2006-8-9 14:07:47作者:janyin导读:
正则表达式可以让用户通过使用一系列的特殊字符构建匹配模式,然后把匹配模式与数据文件、程序输入以及WEB页面的表单输入等目标对象进行比较,根据比较对象中是否包含匹配模式,执行相应的程序。
除了我们以上的元字符之外,正则表达式中还具有另外一种较为独特的专用字符,即定位符。定位符用于规定匹配模式在目标对象中的出现位置。较为常用的定位符包括: "^", "$", "\b" 以及 "\B"。
最后,当用户需要在正则表达式的模式中加入元字符,并查找其匹配对象时,可以使用转义符"\"。例如:/Th\*/,该正则表达式将会与目标对象中的"Th*"而非"The"等相匹配。
^abc 与"abc xyz"匹配,而不与"xyz abc"匹配
abc$ 与"xyz abc"匹配,而不与"abc xyz"匹配。
ab+ 可以匹配"abb"、"abbb"等,但不匹配"ab"。
ab?c? 可以且只能匹配"abc"、"abbc"、"abcc"和"abbcc"
abc|xyz 可匹配 "abc"或 "xyz",而"ab(c|x)yz"匹配 "abcyz"和"abxyz"
a{3,} 匹配"aaa"、"aaaa"等,但不匹配"a"和"aa"。
[^xyz]表示一个否定的字符集。匹配不在此括号中的任何字符。例如:
[^abc] 可以匹配除"a"、"b"和"c"之外的任何字符
[a-z]表示某个范围内的字符,匹配指定区间内的任何字符。例如:
[^m-n]表示某个范围之外的字符,匹配不在指定范围内的字符。例如:
\s 任何白字符,包括空格、制表符、分页符等。等价于"[ \f\n\r\t\v]"
\w 任何单词字符,包括字母和下划线。等价于"[A-Za-z0-9_]"
ve\b 匹配单词"love"等,但不匹配"very"、"even"等
abc\dxyz 匹配"abc2xyz"、"abc4xyz"等,
abc\Dxyz 匹配"abcaxyz"、"abc-xyz"等,
\NUM匹配NUM个(其中NUM为一个正整数),引用回到记住的匹配。例如:
\oNUM匹配n(其中n为一个小于256的八进制换码值)。例如:
\xNUM匹配NUM(其中NUM为一个小于256的十六进制换码值)。例如:
在对正则表达式有了较为全面的了解之后,就可以在Perl,PHP,以及ASP等程式中使用正则表达式了。
下面以PHP语言为例,使用验证用户在线输入的邮件地址以及网址的格式是否正确。PHP 提供了eregi()或ereg()资料处理函数实现字串比对剖析的模式匹配操作ereg()函数的使用格式如下:
if (ereg("^([a-z0-9_-])+@([a-zZ0-9_-])+(\.[a-z0-9_-])+[a-z]{2,3}$",$email))
{ echo "不是合法的E-Mail 地址,请重新输入!";}
我们通过调用自定义正规则判别函式也可以进行检查操作,如下面的网址检验函式:
function VerifyWebSiteAddr ($strWebSiteAddr){
return (eregi ("^([_0-9a-z-]+.)+([0-9a-z-]+.)+[a-z]{2,3}$", $strWebSiteAddr));
var pattern = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/;
alert("不是合法的E-Mail 地址,请重新输入!");
然后在网页中输入信息的表单域<form>标签区域内中加入一行如下代码:
<onSubmit="return verifyAddress(this);">
当按下提交按钮后,首先运行verifyAddress()函式,进行匹配识别,如果满足条件则发送表单信息到目标页面,否则返回错误信息。
事实上,正则表达式的功能远非本文提到的这一点,下次,给大家介绍一种使用正则表达式从任意指定网页中析取任意种类文本信息(如网页中所有的图片文件名)的技巧。
<form action=" abstractSRCfrompage.php3" method="post">
输入网址<input type=text name=filename><br>
<input type=submit name=submit value="提交">
$fp = fopen($filename, "r"); file://若输入不为空,开启本地或者远程档案;
while ($buffer = fgets($fp, 1024)) {
file://查找$source中是否有<img ...src=".../...gif | jpg"> 这样的标记
if(eregi("(<img)+[^<>]+(src=\")+[^\*\"<>|]+(\.)+((gif)|(jpg))+(\")",$source)) {
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://输出析取的图片信息
好了,写好后,将abstractSRCfrompage.php3存到你的服务器指定目录下,启动Apache服务器,在浏览器中打开它,随便输入一个存在的网页名称或是远程URL,看看效果如何。
如果有兴趣,你可以尝试析取HTML文档中的任意感兴趣的信息,如果稍加改装,做一个网站文本搜索引擎岂不更妙?
UBB代码是HTML的一个变种。一般情况下,UBB论坛不允许你使用HTML代码,而只能用UBB代码替代HTML代码。
你也许会问:ASP是怎样把 how are you转换为<b>how are you</b>的呢?
((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等形式出现。初步总结一下就是,链接地址必须符合如下条件:
以http://或者https://或者ftp://等开头(当然还有其它形式,这里只列出主要的)
http://后面必须跟一个单词字符,紧接着单词字符后面的是"."(这样的组合必须出现一次或多次)。紧跟着"."后面的是域名后缀(如net或者com或者cn等,如果是以IP地址的形式出现就可以是数字)
出现完整的链接地址后,还可以出现下一级或者更多级的目录(还要注意个人主页的地址有可能出现"~"符号)
链接地址末尾可以带参数。如典型的页数?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"的字符串可以出现也可以不出现(因为并不是每个网页都带有两个以上的参数。
我们的目的就是要把成对的替换成<b></b>下面来看我们实现它的模板
这里用了"(.+)"来配匹到之间的整个字符串,在替代的时候我们要写成这样
str=checkexp(re,str,"<b>$2</b>")
(注意:checkexp是我自定义的函数,将在后面给出。这个函数将把按照我们提供的模板进行替代。)
也许你会问这里出现一个"$2"是什么东东,呵注意了这个$2可是很重要的,它代表了"(.+)"所配匹的整个字符串。
为什么是$2而不是$1、$3呢?因为$1代表(\[b\])所匹配的""字符串,$3代表(\[\/b\])所匹配的""字符串,显然这里我们需要的是$2而不
下面是我写的一个UBB函数,这个函数基本上能使你的论坛成为一个优秀的UBB代码论坛了。当然,通过改进后,你可以得到一个更强大的U
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)+)*)*)
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\])" ''引用
<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>")