剖析NUTCH爬虫——原文出处:Dissecting The Nutch Crawler
对于局域内网页的抓取,你需要首先编写几个配置文件,例如urls.txt和crawl-urlfilter.txt。然后利用"bin/nutch crawl ..."命令进行抓取。这个命令与程序中的net.nutch.tools.CrawlTool类相对应。
$ bin/nutch admin db -create
$ bin/nutch inject db ...
$ bin/nutch generate db segments
$ bin/nutch fetch ...
$ bin/nutch updatedb ...
$ bin/nutch analyze ...
每个命令对应的java类如下所示:
· admin: net.nutch.tools.WebDBAdminTool
· inject: net.nutch.db.WebDBInjector
· generate: net.nutch.tools.FetchListTool
· fetch: net.nutch.fetcher.Fetcher
· updatedb: net.nutch.tools.UpdateDatabaseTool
· analyze: net.nutch.tools.LinkAnalysisTool
可以发现,这些命令利用的是这些java类的类名或者类名简写,举例来说,下面两个命令的含义就是相同的。
$ bin/nutch admin db -create
$ bin/nutch net.nutch.tools.WebDBAdminTool db -create
下面我们可以通过对这些类的分析来了解NUTCH爬虫的工作机制。
3.CRAWL命令:net.nutch.tools.CrawlTool类
CrawlTool类的作用很好理解,它就是将爬行整个网络的一系列命令操作综合在一起并加以限制条件实现了局域性网络的爬行。它主要包括了两个静态函数和一个main函数。下面是操作细节:
操 作: 对应的函数:
- start logger: LogFormatter.getLogger(...)
(生成日志文件)
- load "crawl-tool.xml" config file: NutchConf.addConfResource(...)
(加载配置文件)
- read arguments from command-line
(读取命令行参数,如爬行深度等)
- create a new web db: WebDBAdminTool.main(...)
(生成一个新网页库)
- add rootURLs into the db: WebDBInjector.main(...)
(将种子URL写入网页库)
- for 1 to depth (=5 by default):
- generate a new segment: FetchListTool.main(...)
- fetch the segment: Fetcher.main(...)
- update the db: UpdateDatabaseTool.main(...)
(爬行网页并更新网页库)
- comment:
"Re-fetch everything to get complete set of incoming anchor texts"
- delete all old segment data: FileUtil.fullyDelete(...)
- make a single segment with all pages:FetchListTool.main(...)
- re-fetch everything: Fetcher.main(...)
(根据网页锚文本爬行链接网页)
- index: IndexSegment.main(...)
(索引)
- dedup: DeleteDuplicates.main(...)
(删除备份)
- merge: IndexMerger.main(...)
(合并索引)
将以上各步还原为NUTCH命令脚本你就会发现,其实它和爬行整个网络所进行的操作是一码事。
- (start logger, etc)
- bin/nutch admin db -create
- bin/nutch inject db ...
- (for 1 to depth:)
- bin/nutch generate ...
- bin/nutch fetch ...
- bin/nutch updatedb ...
- (call net.nutch.FileUtil.fullyDelete(...))
- bin/nutch generate ...
- bin/nutch index ...
- bin/nutch dedup ...
- bin/nutch merge ...
Admin命令的作用主要是对网页库进行管理操作。其命令格式如下:
java net.nutch.tools.WebDBAdminTool db [-create] [-textdump dumpPrefix] [-scoredump] [-top k]
利用spam作为文件夹执行此命令,执行后生产的网页库物理文件如下所示:
$ bin/nutch admin spam -create
$ find spam -type file | xargs ls -l
-rw-r--r-- 1 kangas users 0 Oct 25 18:31 spam/dbreadlock
-rw-r--r-- 1 kangas users 0 Oct 25 18:31 spam/dbwritelock
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/linksByMD5/data
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/linksByMD5/index
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/linksByURL/data
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/linksByURL/index
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/pagesByMD5/data
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/pagesByMD5/index
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/pagesByURL/data
-rw-r--r-- 1 kangas users 16 Oct 25 18:31 spam/webdb/pagesByURL/index
此命令的主要作用就是将新的URL写入网页库,命令格式如下:
我们首先建立spam_url.txt文件,在文件里面写入一个url,然后运行bin/nutch inject命令。结果如下:
$ vi spam_url.txt
$ bin/nutch inject spam -urlfile spam_url.txt
$ find spam -type file | xargs ls -l
-rw-r--r-- 1 kangas users 0 Oct 25 18:57 spam/dbreadlock
-rw-r--r-- 1 kangas users 0 Oct 25 18:57 spam/dbwritelock
-rw-r--r-- 1 kangas users 16 Oct 25 18:57 spam/webdb/linksByMD5/data
-rw-r--r-- 1 kangas users 16 Oct 25 18:57 spam/webdb/linksByMD5/index
-rw-r--r-- 1 kangas users 16 Oct 25 18:57 spam/webdb/linksByURL/data
-rw-r--r-- 1 kangas users 16 Oct 25 18:57 spam/webdb/linksByURL/index
-rw-r--r-- 1 kangas users 89 Oct 25 18:57 spam/webdb/pagesByMD5/data
-rw-r--r-- 1 kangas users 97 Oct 25 18:57 spam/webdb/pagesByMD5/index
-rw-r--r-- 1 kangas users 115 Oct 25 18:57 spam/webdb/pagesByURL/data
-rw-r--r-- 1 kangas users 58 Oct 25 18:57 spam/webdb/pagesByURL/index
-rw-r--r-- 1 kangas users 17 Oct 25 18:57 spam/webdb/stats
此命令的主要作用是为fetch命令的执行做准备,产生其所需的新segments。格式:
FetchListTool 被用于生成一个或者多个segments。那Segmnet是什么呢?
Segment代表一个网页集合,这个集合中的网页被作为一个小的单元统一地进行抓取和索引。它里面存储的数据主要有三个类型:
· a "fetchlist": 将要被抓取的网页的名称列表
· the "fetcher output": 被抓取回来的网页的文件集合
FetchListTool.main()在CrawlTool.main()中被调用,调用时带了两个参数(dir + "/db", dir + "/segments"),在处理这些参数的基础上返回一个自身的实例对象flt.emitFetchList()。运行此命令,详情如下所示:
$ bin/nutch generate spam spam_segments
$ find spam -type file | xargs ls -l
-rw-r--r-- 1 kangas users 0 Oct 25 20:18 spam/dbreadlock
-rw-r--r-- 1 kangas users 0 Oct 25 20:18 spam/dbwritelock
-rw-r--r-- 1 kangas users 16 Oct 25 20:18 spam/webdb/linksByMD5/data
-rw-r--r-- 1 kangas users 16 Oct 25 20:18 spam/webdb/linksByMD5/index
-rw-r--r-- 1 kangas users 16 Oct 25 20:18 spam/webdb/linksByURL/data
-rw-r--r-- 1 kangas users 16 Oct 25 20:18 spam/webdb/linksByURL/index
-rw-r--r-- 1 kangas users 89 Oct 25 20:18 spam/webdb/pagesByMD5/data
-rw-r--r-- 1 kangas users 97 Oct 25 20:18 spam/webdb/pagesByMD5/index
-rw-r--r-- 1 kangas users 115 Oct 25 20:18 spam/webdb/pagesByURL/data
-rw-r--r-- 1 kangas users 58 Oct 25 20:18 spam/webdb/pagesByURL/index
-rw-r--r-- 1 kangas users 17 Oct 25 20:18 spam/webdb/stats
$ find spam_segments/ -type file | xargs ls -l
-rw-r--r-- 1 kangas users 113 Oct 25 20:18 spam_segments/20041026001828/fetchlist/data
-rw-r--r-- 1 kangas users 40 Oct 25 20:18 spam_segments/20041026001828/fetchlist/index
我们可以看到,网页库的文件夹(spam)没有变化,但是segment的文件夹被创建了,里面包括data和index文件。
差不多了,未完待续,下次详细分析fetch。
Fetch命令:net.nutch.fetcher.Fetcher
此命令的作用就是抓取网页,但是需要注意的是,抓取的单位不是单个的网页,而是同属于一个segment的网页集合。命令格式:
在进行fetch之前,我们已经生成了webdb,在其中初始化写入了URL,并且生成了segment留待写入抓取内容。我们首先看一下Fetcher类,然后运行一下看其效果。
net.nutch.fetcher.Fetcher类依靠下面几个类:
l FetcherThread, an inner class
l net.nutch.parse.ParserFactory
l net.nutch.plugin.PluginRepository
Fetcher类中的main()函数的功能主要是读取参数,实例化一个Fetcher对象,设置选项,然后调用run()函数。Fetcher类的构造函数很简单,它只是实例化了所有的输入输出流:
实例对象 | 类 | 参数 |
fetchList | ArrayFile.Reader | (dir, "fetchlist") |
fetchWriter | ArrayFile.Writer | (dir, "fetcher", FetcherOutput.class) |
contentWriter | ArrayFile.Writer | (dir, "content", Content.class) |
parseTextWriter | ArrayFile.Writer | (dir, "parse_text", ParseText.class) |
parseDataWriter | ArrayFile.Writer | (dir, "parse_data", ParseData.class) |
FetcherThread.run()实例化了一个新的FetchListEntry实例名叫”fle”,然后执行以下循环:
1. 如果出现错误,记录并退出循环
2. 从FetchList中取得下一个URL集,如果取得结果为空,退出循环
3. 从FetchListEntry解析出URL
4. 如果FetchListEntry没有被标记为”fetch”(未抓取此URL的网页),那么调用this.handleNoFetch()函数,设置status=1。然后逐步执行:
Ø 取得此URL的MD5摘要
Ø 建立FetcherOutput(fle, hash, status)
Ø 建立空的Content、ParseText和 ParseData对象
Ø 利用这些对象调用Fetcher.outputPage()函数
5. 如果标记为”fetch”(已抓取此URL的网页),调用ProtocolFactory并取得符合此URL的协议和内容对象。
6. 调用Call this.handleFetch(url, fle, content).然后逐步执行:
Ø 调用符合此内容类型的ParserFactory.getParser()
Ø 执行parser.getParse(content)
Ø 利用新建立的FetcherOutput和URL的MD5摘要,产生的内容对象和已解析的ParseText调用Fetcher.outputPage()函数
7. 循环100次,在log中记录