创业以来尝试过好几个创业项目,在每次 bootstrap的时候,往往都需要借助于一些Internet上的内容,这里不可避免的就需要写一些简单的爬虫来抓取一些数据来完成项目的初期引导。这些小的爬虫对于我学习.Net,Http Protocol, Framework Design, Design Patterns提供了很多的帮助。爬虫版本的一次一次refactoring和upgrade都往往能够加深我对于某些领域的知识的掌握。
Open source方面比较有名的爬虫项目有Nutch和Heritrix。Nutch可谓出身名门,Lucene, Hadoop, Hbase等都给Nutch增色不少。Heritrix配置有点麻烦,我下载过source code,不过只是大概过了一下,没有具体debug它的具体源代码。前几天也看到园子里arrowcat和Phinecos(洞庭散人)两位同志做过一点介绍。由于个人是没有这方面深入的需求,所以一直都是靠自己个人来思考如何完成一个简单爬虫的设计和编码。这两个项目都是Java写的,所以对我们很多.Net fans来说还是不大方便。为此,我把我在写爬虫过程中遇到问题,solutions特别是一些需求总结给大家做些基本的介绍,希望对一些人有所帮助,对我个人也算是一个不错的总结。
第一,请求发送。一般我写的爬虫都是走http协议的,当然你可以用TCP协议来模拟http request。
· http request 发送和response接受
在.Net Framework中HttpWebRequest和HttpWebResponse两个类的封装使得http发送变得很容易,不过这两个类设计到很多http protocol方面的知识,所以不熟悉的同志还需深入挖一挖。一般都要提供GET和POST两个方法。这里可能会出现gzip压缩的解压,有些网站对于useragent要求不能为空等问题。
· 网站编码方式
我们常见的编码方式一般是UTF-8,GBK或者GB2312编码,这里如果网站变化多就需要自动检测出每个网站的编码方式,不然编码搞错了,后果很严重。这个我没有做过,应该拿一个request的响应然后分析一下那个结果的http header就可以确定出来了。如果网站可以简单枚举,自己手工设置一下就可以了。
· Request发送频率
如果你的request太多,容易给对方机器加大负载,可能会被server上某些软件屏蔽掉,最夸张会让对方管理员发现,所以要小心设置一个合适的请求延迟。不知道google的spider的参数怎么得来的,应该也是经验的慢慢积累来着。
· Http分析 :很多时候你写spider之前是需要分析一下爬虫源头,这里你需要借助一下工具来完成任务,我常用的是HttpWatch,Fiddler2, IE Developer Toolbar, FireBug,类似还有一些,例如Microsoft Network Monitor, Web Development Helper等。有了这些工具的帮助使得你能够快速了解要抓网页的结构和request的一些细节。
第二,内容解析。通过request我们拿到response后就需要根据其内容的格式进行解析。我以前写过关于hotmail, gmail, yahoo! Mail, 163的联系人导入程序,这些可能需要你去解析xml, json等格式。还有一个关于blog的rss解析问题,这里都有一些规则,如果能找到open source的library最好,没有只好读点文档或者研究其规则自己尝试解析了。对于xml解析,使用SAX要比DOM快很多。RSS+ATOM可能有.net方面的开源类库,我以前是自己解析的,这个就很难把所有情形cover了,risk还是蛮高的。对于最常见的Html解析您可以选用htmlparser这样的类库,当然也可以自己从头来,我主要是利用正则表达式和.Net Framework来搞定这些问题的,例如从文档中抽取出所有的html anchors,去除所有的html tags,变相对路径url成绝对路径url,截取一部分内容到一句话结尾等。还有可能遇到的情形就是垂直搜索,例如抓电影信息,可能对导演,演员,播放长度,图片要求很准确,这里你的spider extractor就需要依赖对方的html格式,如果对方有rss等API那就爽了。没有你只能想办法降低对html格式的依赖程度了,也可能需要提供一些解析器失效的报警或日志机制。
列一下可能需要的代码:
抽取所有链接:
Regex htmlLinkReg = new Regex(@"(href)[ ]*=[ ]*[""'][^""'> ]+[""'> ]", RegexOptions.IgnoreCase);
去除html标签和 这样的编码:
result = Regex.Replace(html, @"<(.[^>]*)>?", replacedWord, RegexOptions.IgnoreCase);
result = Regex.Replace(result, @"&\w{2,7};", "");
第三,分布式的架构。这里可能涉及到多客户端的部署,因为很多时候IP算是稀有资源,同一个IP来抓速度还是很慢的,又不敢太快。呵呵。我写过一个分布式爬虫,抓了数千万数据下来。Client我一般是用winform写的,后台开点线程来完成各种爬行任务,它每次从服务器获取一点任务,然后去发送request,解析结果,最后再通过http request来上传爬行结果。这种简单的架构还是很容易scale的。
· 爬行历史:你可能需要将spider的url爬行历史记录保存下来,一方面可以避免重复爬行;另外也可以判断这些url是否在上次爬行后更新过。
· 网站更新:如果您需要持续抓取某个网站的内容,你可能需要把首页或者更新相关的页面作为源头,然后抽取出页面所有链接,然后filter出你要解析的页面和已经爬过的页面,然后使用一个队列来存储这些源头,通过进一步加深爬虫深度来找到所有可能新的内容页面,这里你可以使用广度优先搜索或者DFS来递归深入。这里你可能会遇到输入输出速率不匹配的情形,你可能需要依赖一些队列模拟,当然也可以自己通过数据库来实现。
· 具体页面爬行:如果您通过数据库等来存储具体内容页面的源头,对于分布式的应用,你可能需要考虑好同步问题,你可以依赖于.Net提供的一些concurrency方法来解决可能的并发重复问题,例如Monitor, Semaphore, Event, Mutex等.
以上几点是我关于一个简单爬虫设计可能遇到的问题的总结。希望对于需要的有所帮助。