|
图 1. 清单 5 中的 Ruby 脚本发送的 E-mail 警告
至此对 scraper 的介绍就告一段落,接下来将深入地了解一下 Web spider 的构建。
例子 4: Web 站点爬虫
在最后这个例子中,将探索一下在 Web 站点上爬行的 Web spider。为了安全起见,我将避免在该站点之外浪费时间,而只会深入研究一个 Web 页面。
要在 Web 站点上爬行并访问这个站点上所提供的链接,必须要对 HTML 页面进行解析。如果可以成功解析 Web 页面,就可以确定到其他资源的链接,这些链接有些指定的是本地资源(文件),而有些则会代表非本地的资源(例如到其他 Web 页面的链接)。
要在 Web 上爬行,需要从一个给定的 Web 页面开始,确定这个页面中的所有链接,将它们放入一个等待访问的队列中进行排序,然后使用等待访问队列中的第一项来重复这个处理过程。这会产生广度优先遍历(与优先处理首先找到的第一个链接不同,后者是一种深度优先遍历)。
如果能够避免非本地的链接而只访问本地 Web 页面,就可以为这个单一 Web 站点提供 Web 爬虫了,如清单 7 所示。在本例中,使用 Python 语言来代替 Ruby 语言,这样做是为了利用 Python 非常有用的 HTMLParser 类。
清单 7. 简单的 Python Web 站点爬虫(minispider.py)
#!/usr/local/bin/python
import httplib
import sys
import re
from HTMLParser import HTMLParser
class miniHTMLParser( HTMLParser ):
viewedQueue = []
instQueue = []
def get_next_link( self ):
if self.instQueue == []:
return ''
else:
return self.instQueue.pop(0)
def gethtmlfile( self, site, page ):
try:
httpconn = httplib.HTTPConnection(site)
httpconn.request("GET", page)
resp = httpconn.getresponse()
resppage = resp.read()
except:
resppage = ""
return resppage
def handle_starttag( self, tag, attrs ):
if tag == 'a':
newstr = str(attrs[0][1])
if re.search('http', newstr) == None:
if re.search('mailto', newstr) == None:
if re.search('htm', newstr) != None:
if (newstr in self.viewedQueue) == False:
print " adding", newstr
self.instQueue.append( newstr )
self.viewedQueue.append( newstr )
else:
print " ignoring", newstr
else:
print " ignoring", newstr
else:
print " ignoring", newstr
def main():
if sys.argv[1] == '':
print "usage is ./minispider.py site link"
sys.exit(2)
mySpider = miniHTMLParser()
link = sys.argv[2]
while link != '':
print "\nChecking link ", link
# Get the file from the site and link
retfile = mySpider.gethtmlfile( sys.argv[1], link )
# Feed the file into the HTML parser
mySpider.feed(retfile)
# Search the retfile here
# Get the next link in level traversal order
link = mySpider.get_next_link()
mySpider.close()
print "\ndone\n"
if __name__ == "__main__":
main()
|
这个爬虫的基本设计是加载第一个链接并将其放入一个队列。此队列就是下一个要询问 (next-to-interrogate) 队列。当一个链接被选中时,所发现的任何新链接都被加入相同的队列中。这提供了一种广度优先的搜索。另外还维护了一个已查看过的队列以防止再次访问过去已经查看过的链接。基本上就这些,很多实际工作都可以由 HTML 解析器来完成。
先是从 Python 的 HTMLParser 类获取一个新类 miniHTMLParser。这个类可以实现几个功能。首先,它可以用作 HTML 解析器,只要碰到开始的 HTML 标记都会提供一个回调方法 (handle_starttag)。其次,这个类还可以用来访问在爬行中所碰到的链接 (get_next_link) 并检索这个链接所代表的文件(在本例中是一个 HTML 文件)。
这个类中还包含了两个实例变量:viewedQueue,其中包含了到目前为止已经检查过的链接;instQueue,表示将要被审查的链接。
正如您所见,类方法非常简单。get_next_link 方法检查 instQueue 是否为空,并返回 ''。否则,就通过 pop 方法返回下一项。gethtmlfile 方法使用 HTTPConnectionK 连接到站点上并返回指定页面的内容。最后,对 Web 页面中的每个开始标记都调用 handle_starttag(它是通过 feed 方法传递给 HTML 解析器的)。在这个函数中,检查该链接是否是非本地链接(如果链接中包含 http),是否是 e-mail 地址(如果包含 mailto),以及链接中是否包含 'htm',如果包含则说明它(有很大的可能)是一个 Web 页面。另外还会检查以确保之前没有访问过这个链接;否则,就将这个链接加载到已经审查过的队列中。
共6页: 上一页 [1] [2] [3] 4 [5] [6] 下一页
|