??xml version="1.0" encoding="utf-8" standalone="yes"?>1. 常见的中文分词器有:极易分词?MMAnalyzer) ?/span>"庖丁分词"分词?PaodingAnalzyer)、IKAnalyzer {等。其?nbsp;MMAnalyzer ?nbsp;PaodingAnalzyer 不支?nbsp;lucene3.0及以后版本?br>
使用方式都类|在构建分词器?br>
Analyzer analyzer = new [My]Analyzer();
2. q里只示?nbsp;IKAnalyzerQ目前只有它支持Lucene3.0 以后的版本?nbsp;
首先需要导?nbsp;IKAnalyzer3.2.0Stable.jar ?br>
3. CZ代码
view plaincopy to clipboardprint?
public class AnalyzerTest
{
@Test
public void test() throws Exception
{
String text = "An IndexWriter creates and maintains an index.";
/**//* 标准分词器:单子分词 */
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
testAnalyzer(analyzer, text);
String text2 = "试中文环境下的信息?/span>";
testAnalyzer(new IKAnalyzer(), text2); // 使用IKAnalyzerQ词库分?nbsp;
}
/** *//**
* 使用指定的分词器Ҏ(gu)定的文本q行分词Qƈ打印l果
*
* @param analyzer
* @param text
* @throws Exception
*/
private void testAnalyzer(Analyzer analyzer, String text) throws Exception
{
System.out.println("当前使用的分词器Q?/span>" + analyzer.getClass());
TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text));
tokenStream.addAttribute(TermAttribute.class);
while (tokenStream.incrementToken())
{
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
System.out.println(termAttribute.term());
}
}
}
public class AnalyzerTest
{
@Test
public void test() throws Exception
{
String text = "An IndexWriter creates and maintains an index.";
/**//* 标准分词器:单子分词 */
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
testAnalyzer(analyzer, text);
String text2 = "试中文环境下的信息?/span>";
testAnalyzer(new IKAnalyzer(), text2); // 使用IKAnalyzerQ词库分?/span>
}
/** *//**
* 使用指定的分词器Ҏ(gu)定的文本q行分词Qƈ打印l果
*
* @param analyzer
* @param text
* @throws Exception
*/
private void testAnalyzer(Analyzer analyzer, String text) throws Exception
{
System.out.println("当前使用的分词器Q?/span>" + analyzer.getClass());
TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text));
tokenStream.addAttribute(TermAttribute.class);
while (tokenStream.incrementToken())
{
TermAttribute termAttribute = tokenStream.getAttribute(TermAttribute.class);
System.out.println(termAttribute.term());
}
}
}
3. 如何扩展词库Q很多情况下Q我们可能需要定制自q词库Q例?nbsp;XXX 公司Q我们希望这能被分词器识别,q拆分成一个词?br>
IKAnalyzer 可以很方便的实现我们的这U需求?br>
新徏 IKAnalyzer.cfg.xml
view plaincopy to clipboardprint?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<!-- 1Q文件要?nbsp;UTF-8 ~码?/span>2Q一行写一个词 -->
<!--用户可以在这里配|自q扩展字典-->
<entry key="ext_dict">/mydict.dic</entry>
</properties>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<!-- 1Q文件要?nbsp;UTF-8 ~码?/span>2Q一行写一个词 -->
<!--用户可以在这里配|自q扩展字典-->
<entry key="ext_dict">/mydict.dic</entry>
</properties>
解析Q?br>
<entry key="ext_dict">/mydict.dic</entry> 扩展了一个自q词典Q名字叫 mydict.dic
因此我们要徏一个文本文Ӟ名ؓQmydict.dic Q此处用的 .dic q必须Q?br>
在这个文本文仉写入Q?br>
北京XXXXU技有限公司
q样添加了一个词汇?br>
如果要添加多个,则新起一行:
词汇一
词汇?br>
词汇?br>
需要注意的是,q个文g一定要使用 UTF-8~码
4. 停用词:
有些词在文本中出现的频率非常高,但是Ҏ(gu)本所携带的信息基本不产生影响Q例如英文的"a、an、the、of"Q或中文?/span>"的、了、着"Q以及各U标点符LQ这L词称为停用词Qstop wordQ?br>
文本l过分词之后Q停用词通常被过滤掉Q不会被q行索引。在索的时候,用户的查询中如果含有停用词,索系l也会将其过滤掉Q因为用戯入的查询字符串也要进行分词处理)?br>
排除停用词可以加快徏立烦引的速度Q减烦引库文g的大?br>
IKAnalyzer 中自定义停用词也非常方便Q和配置 "扩展词库" 操作cdQ只需要在 IKAnalyzer.cfg.xml 加入如下配置Q?br>
<entry key="ext_stopwords">/ext_stopword.dic</entry>
同样q个配置也指向了一个文本文?nbsp;/ext_stopword.dic Q后~名Q意)Q格式如下:
?br>
?br>
?br>
?br>
本文来自CSDN博客Q{载请标明出处Qhttp://blog.csdn.net/wenlin56/archive/2010/12/13/6074124.aspx
?nbsp;上一部分 中,(zhn)了解到如何~写一?nbsp;spider E序来进行网늚爬取Q作?nbsp;spider 的爬取结果,我们获得了一个按照一定格式存储的原始|页库,原始|页库也是我们第二部分网预处理的数据基。网预处理的主要目标是原始网通过一步步的数据处理变成可方便搜烦的数据Ş式。下面就让我们逐步介绍|页预处理的设计和实现?br>
预处理模块的整体l构
预处理模块的整体l构如下Q?br>
?nbsp;1. 预处理模块的整体l构
通过 spider 的收集,保存下来的网信息具有较好的信息存储格式Q但是还是有一个缺点,是不能按照|页 URL 直接定位到所指向的网c所以,在第一个流E中Q需要先建立|页的烦引,如此通过索引Q我们可以很方便的从原始|页库中获得某个 URL 对应的页面信息。之后,我们处理|页数据Q对于一个网,首先需要提取其|页正文信息Q其ơ对正文信息q行分词Q之后再Ҏ(gu)分词的情况徏立烦引和倒排索引Q这P|页的预处理也全部完成。可能读者对于其中的某些专业术语会有一些不明白之处Q在后箋详述各个程的时候会l出相应的图或者例子来帮助大家理解?br>
回页?br>
建立索引|页?br>
原始|页库是按照格式存储的,q对于网늚索引建立提供了方便,下图l出了一条网信息记录:
清单 1. 原始|页库中的一条网记?br>
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 之前的记?/span>
version:1.0 // 记录头部
url:http://ast.nlsde.buaa.edu.cn/
date:Mon Apr 05 14:22:53 CST 2010
IP:218.241.236.72
length:3981
<!DOCTYPE …… // 记录数据部分
<html> …… </html>
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 之后的记?/span>
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
我们采用“|页库名—偏U?#8221;的信息对来定位库中的某条|页记录。由于数据量比较大,q些索引|页信息需要一U保存的Ҏ(gu)QdySE 使用数据库来保存q些信息。数据库们采?nbsp;mysqlQ配?nbsp;SQL-Front 软g可以Lq行囑Ş界面的操作。我们用一个表来记录这些信息,表的内容如下Qurl、content、offset、raws。URL 是某条记录对应的 URLQ因为烦引数据库建立之后Q我们是通过 URL 来确定需要的|页的;raws ?nbsp;offset 分别表示|页库名和偏Ud|q两个属性唯一定了某条记录,content 是网内容的摘要Q网늚数据量一般较大,把网늚全部内容攑օ数据库中昑־不是很实际,所以我们将|页内容?nbsp;MD5 摘要攑օ?nbsp;content 属性中Q该属性相当于一个校验码Q在实际q用中,当我们根?nbsp;URL 获得某个|页信息是,可以获得的|页?nbsp;MD5 摘要然后?nbsp;content 中的值做一个匹配,如果一样则|页获取成功Q如果不一P则说明网获取出现问题?br>
q里单介l一?nbsp;mySql 的安装以及与 Java 的连接:
安装 mySqlQ最好需要三个组ӞmySqlQmySql-frontQmysql-connector-java-5.1.7-bin.jarQ分别可以在|络中下载。注意:安装 mySql ?nbsp;mySql-front 的时候要版本对应QMySql5.0 + MySql-Front3.2 ?nbsp;MySql5.1 + MySql-Front4.1Q这个组合是不能qQ可以根据相应的版本h下蝲Q否则会?#8220;‘ 10.000000 ’ ist kein gUltiger Integerwert ”的错误?br>
导入 mysql-connector-java-5.1.7-bin.jar ?nbsp;eclipse 的项目中Q打开 eclipseQ右键点需要导?nbsp;jar 包的?nbsp;目名Q选属性(properties)Q再?nbsp;java 构徏路径Qjava Build Path)Q后在右侧点 (libraries)Q?nbsp;add external JARsQ之后选择你要导入?nbsp;jar 包确定?br>
接着可以用代码来测试与 mySql 的连接了Q代码见本文附带?nbsp;testMySql.java E序Q这里限于篇q就不在赘述?br>
对于数据库的操作Q我们最好进行一定的装Q以提供l一的数据库操作支持Q而不需要在其他的类中显C的q行数据库连接操作,而且q样也就不需要徏立大量的数据库连接从而造成资源的浪费,代码详见 DBConnection.java。主要提供的操作是:建立q接、执?nbsp;SQL 语句、返回操作结果?br>
介绍了数据库的相x作时候,现在我们可以来完成网늃引库的徏立过E。这里要说明的是Q第一条记录的偏移?nbsp;0Q所以在当前记录 record 处理之前Q该记录的偏UL已经计算出来的,处理 record 的意义在于获得下一个记录在|页库中的偏UR假讑ֽ?nbsp;record 的偏UMؓ offsetQ定位于头部的第一条属性之前,我们通过d记录的头部和记录的数据部分来得到该记录的长度 lengthQ从而,offset+length 即ؓ下一条记录的偏移倹{读取头部和d记录都是通过数据间的I来标识的Q其伪代码如下:
清单 2. 索引|页库徏?br>
For each record in Raws do
begin
d record 的头部和数据Q从头部中抽?nbsp;URLQ?br>
计算头部和数据的长度Q加到当前偏Udg得到新的偏移Q?br>
?nbsp;record 中数据中计算?nbsp;MD5 摘要|
数据插入数据库中,包括QURL、偏UR数?nbsp;MD5 摘要、RawsQ?br>
endQ?br>
(zhn)可能会?nbsp;MD5 摘要法有些疑惑Q这是什么?q有什么用Q?nbsp;Message Digest Algorithm MD5Q中文名为消息摘要算法第五版Qؓ计算机安全领域广泛用的一U散列函敎ͼ用以提供消息的完整性保护。MD5 的典型应用是对一D信?nbsp;(Message) 产生一?nbsp;128 位的二进制信息摘?nbsp;(Message-Digest)Q即?nbsp;32 ?nbsp;16 q制数字Ԍ以防止被改。对于我们来_比如通过 MD5 计算Q某个网|据的摘要?nbsp;00902914CFE6CD1A959C31C076F49EA8Q如果我们Q意的改变q个|页中的数据Q通过计算之后Q该摘要׃改变Q我们可以将信息?nbsp;MD5 摘要视作信息的指U信息。所以,存储该摘要可以验证之后获取的|页信息是否与原始网一致?br>
?nbsp;MD5 法要的叙述可以为:MD5 ?nbsp;512 位分l来处理输入的信息,且每一分组又被划分?nbsp;16 ?nbsp;32 位子分组Q经q了一pd的处理后Q算法的输出由四?nbsp;32 位分l组成,这四个 32 位分l联后生成一?nbsp;128 位散列倹{其?#8220;一pd的处?#8221;即ؓ计算程QMD5 的计流E比较多Q但是不难,同时也不隑֮玎ͼ(zhn)可以直接用网上现有的 java 版本实现或者用本教程提供的源码下载中?nbsp;MD5 cR对?nbsp;MD5Q我们知道其功能Q能使用可以,具体的每个步骤的意义不需要深入理解?br>
回页?br>
正文信息抽取
PageGetter
在正文信息抽取之前,我们首先需要一个简单的工具c,该工L可以取出数据库中的内容ƈ且去原始|页集中获得|页信息QdySE 对于该功能的实现?nbsp;originalPageGetter.java 中,该类通过 URL 从数据库中获得该 URL 对应的网|据的所在网库名以及偏U,然后可以根据偏ULd该网늚数据内容Q同样以原始|页集中各记录间的空行作为数据内容的l束标记Q读取内容之后,通过 MD5 计算当前d的内容的摘要Q校验是否与之前的摘要一致。对于偏Uȝ使用QBufferedReader cL供一?nbsp;skip(int offset) 的函敎ͼ其作用是跌文档中,从当前开始计的 offset 个字W,用这个函数我们就可以定位到我们需要的记录?br>
清单 3. 获取原始|页库中内容
public String getContent(String fileName, int offset)
{
String content = "";
try
{
FileReader fileReader = new FileReader(fileName);
BufferedReader bfReader = new BufferedReader(fileReader);
bfReader.skip(offset);
readRawHead(bfReader);
content = readRawContent(bfReader);
} catch (Exception e)
{e.printStackTrace();}
return content;
}
上述代码中,省略?nbsp;readRawHead ?nbsp;readRawContent 的实玎ͼq些都是基本?nbsp;I/O 操作Q详见所附源码?br>
正文抽取
对于获得的单个网|据,我们可以进行下一步的处理Q首先要做的是正文内容的抽取,从而剔除网中的标{ֆ容,q一步的操作主要采用正则表达式来完成。我们用正则表达式来匚w html 的标{,q且把匹配到的标{ֈ除,最后,剩下的内容就是网|文。限于篇q,我们以过?nbsp;script 标签为示例,其代码如?nbsp;:
清单 4. 标签qo
public String html2Text(String inputString)
{
String htmlStr = inputString; // ?nbsp;html 标签的字W串
Pattern p_script; Matcher m_script;
try
{
String regEx_script = "<script[^>]*?>[\\s\\S]*?</script>";
p_script = Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
m_script = p_script.matcher(htmlStr);
htmlStr = m_script.replaceAll(""); // qo script 标签
}catch(Exception e)
{e.printStackTrace();}
return htmlStr;// q回文本字符?nbsp;
}
通过一pd的标{过滤,我们可以得到|页的正文内容,可以用于下一步的分词了?br>
回页?br>
分词
中文分词是指一个汉字序列切分成一个一个单独的词,从而达到计机可以自动识别的效果。中文分词主要有三种Ҏ(gu)Q第一U基于字W串匚wQ第二种Z语义理解Q第三种Zl计。由于第二和W三U的实现需要大量的数据来支持,所以我们采用的是基于字W串匚w的方法?br>
Z字符串匹配的Ҏ(gu)又叫做机械分词方法,它是按照一定的{略待分析的汉字串与一?#8220;充分大的”机器词典中的词条q行配,若在词典中找到某个字W串Q则匚w成功Q识别出一个词Q。按照扫描方向的不同Q串匚w分词Ҏ(gu)可以分ؓ正向匚w和逆向匚wQ按照不同长度优先匹配的情况Q可以分为最大(最长)匚w和最(最短)匚w。常用的几种机械分词Ҏ(gu)如下Q?br>
正向减字最大匹配法Q由左到右的方向Q;
逆向减字最大匹配法Q由叛_左的方向Q;
最切分(使每一句中切出的词数最)Q?br>
双向最大减字匹配法Q进行由左到叟뀁由叛_左两ơ扫描)Q?br>
我们采用其中的正向最大匹配法。算法描q如下:输入gؓ一个中文语?nbsp;SQ以及最大匹配词 n
?nbsp;S 中前 n 个字Q根据词典对其进行匹配,若匹配成功,?nbsp;3Q否则{ 2Q?br>
n = n – 1Q如?nbsp;n ?nbsp;1Q{ 3Q否则{ 1Q?br>
?nbsp;S 中的?nbsp;n 个字作ؓ分词l果的一部分QS 除去?nbsp;n 个字Q若 S 为空Q{ 4Q否则,?nbsp;1Q?br>
法l束?br>
需要说明的是,在第三步的v始,n 如果不ؓ 1Q则意味着有匹配到的词Q而如?nbsp;n ?nbsp;1Q我们默?nbsp;1 个字是应该进入分词结果的Q所以第三步可以前 n 个字作ؓ一个词而分割开来。还有需要注意的是对于停用词的过滤,停用词即汉语?#8220;的,了,和,?#8221;{字词,在搜索引擎中是忽略的Q所以对于分词后的结果,我们需要在用停用词列表q行一下停用词qo?br>
(zhn)也许有疑问Q如何获得分词字典或者是停用词字典。停用词字典比较好办Q由于中文停用词数量有限Q可以从|上获得停用词列表,从而自己徏一个停用词字典Q然而对于分词字典,虽然|上有许多知名的汉字分词软gQ但是很有分词的字典提供,q里我们提供一些在 dySE 中用的分词字典l?zhn)。在E序使用q程中,分词字典可以攑օ一个集合中Q这样就可以比较方便的进行比对工作?br>
分词的结果对于搜索的_և性有着臛_重要的媄响,好的分词{略l常是由若干个简单算法拼接而成的,所以?zhn)也可以试着实现双向最大减字匹配法来提高分词的准确率。而如果遇到歧义词l,可以通过字典中附带的词频来决定哪U分词的l果更好?br>
回页?br>
倒排索引
q个章节我们为?zhn)讲解预处理模块的最后两个步骤,索引的徏立和倒排索引的徏立。有了分词的l果Q我们就可以获得一个正向的索引Q即某个|页以及其对应的分词l果。如下图所C:
?nbsp;2. 正向索引
?nbsp;3. 倒排索引
在本文的开_我们建立了烦引网库Q用于通过 URL 可以直接定位到原始网库中该 URL 对应的数据的位置Q而现在的正向索引Q我们可以通过某个|页?nbsp;URL 得到该网늚分词信息。获得正向烦引看似对于我们的卛_q行的查询操作没有什么实际的帮助Q因为查询服务是通过关键词来获得|页信息Q而正向烦引ƈ不能通过分词l果反查|页信息。其实,我们建立正向索引的目的就是通过{的操作徏立倒排索引。所谓倒排是相对于正向烦引中|页——分词结果的映射方式Q采用分词——对应的|页q种映射方式。与?nbsp;2 相对应的倒排索引如上?nbsp;3 所C?br>
接下来我们分析如何从正向索引来得到倒排索引。算法过E如下:
对于|页 iQ获取其分词列表 ListQ?br>
对于 List 中的每个词组Q查看倒排索引中是否含有这个词l,如果没有Q将q个词组插入倒排索引的烦引项Qƈ网?nbsp;i 加到其烦引gQ如果倒排索引中已l含有这个词l,直接网?nbsp;i 加到其烦引gQ?br>
如果q有|页未分析Q{ 1Q否则,l束
建立倒排索引的算法不隑֮玎ͼ主要是其中数据结构的选用Q在 dySE 中,正向索引和倒排索引都是采用 HashMap 来存储,映射中正向烦引的键是采用|页 URL 对应的字W串Q而倒排索引是采用分词词l,映射中的|前者是一个分词列表,后者是一?nbsp;URL 的字W串列表。这里可以采用一个优化,分别建立两个表,按照标号存储分词列表?nbsp;URL 列表Q这P索引中的值就可以使用整型变量列表来节省空间?br>
回页?br>
初步实验
到目前ؓ止,虽然我们q没有正式的查询输入界面以及l果q回面Q但q丝毫不影响我们来对我们的搜索引擎进行初步的实验。在倒排索引建立以后Q我们在E序中获得一个倒排索引的实例,然后定义一个搜索的字符Ԍ直接在倒排索引中遍历这个字W串Q然后返回该词组所指向的倒排索引中的 URL 列表卛_?br>
回页?br>
结
|页的预处理是搜索引擎的核心部分Q徏立烦引网库是ؓ了网|据更方便的从原始|页库中获取Q而抽取正文信息是后箋操作的基。从分词开始就正式涉及到搜索引擎中文本数据的处理,分词的好坏以及效率很大程度上军_着搜烦引擎的精性,是非帔R要关注的一点,而倒排索引时根据分词的l果建立的一?#8220;词组——对应网列?#8221;映射Q倒排索引是网|索的最关键数据l构Q搜索引擎执行的速度与倒排索引的徏立以及倒排索引的搜索方式息息相兟?br>
回页?br>
后箋内容
在本pd的第三部分中Q?zhn)了解到如何从创建网,从网中输入查询信息通过倒排索引的搜索完成结果的q回Qƈ且完成网|名的功能?br>
自己动手写一个搜索引擎,xq有?nbsp;coolQ在界面上输入关键词Q点L索,得到自己惌的结果;那么它还可以做什么呢Q也许是自己的网站需要一个站内搜索功能,抑或是对于硬盘中文档的搜?nbsp;—?nbsp;最重要的是Q是不是觉得众多 IT 公司都在向你招手呢?如果你心动了Q那么,Let's GoQ?/span>
q里首先要说明?nbsp;Java 语言而不?nbsp;C/C++ {其它语a的原因,因ؓ Java 中提供了对于|络~程众多的基包和c,比如 URL cRInetAddress cR正则表辑ּQ这为我们的搜烦引擎实现提供了良好的基础Q我们可以专注于搜索引擎本w的实现Q而不需要因些基cȝ实现而分心?br>
q个分三部分的系列将逐步说明如何设计和实C个搜索引擎。在W一部分中,(zhn)将首先学习搜烦引擎的工作原理,同时了解其体pȝ构,之后讲解如何实现搜索引擎的W一部分Q网l爬虫模块,卛_成网|集功能。在pd的第二部分中Q将介绍预处理模块,卛_何处理收集来的网,整理、分词以及烦引的建立都在q部分之中。在pd的第三部分中Q将介绍信息查询服务的实玎ͼ主要是查询界面的建立、查询结果的q回以及快照的实现?br>
dySE 的整体结?br>
在开始学习搜索引擎的模块实现之前Q?zhn)需要了?nbsp;dySE 的整体结构以及数据传输的程。事实上Q搜索引擎的三个部分是相互独立的Q三个部分分别工作,主要的关pM现在前一部分得到的数据结果ؓ后一部分提供原始数据。三者的关系如下图所C:
?nbsp;1. 搜烦引擎三段式工作流E?br>
在介l搜索引擎的整体l构之前Q我们借鉴《计机|络——自向下的Ҏ(gu)描述因特|特艌Ӏ一书的叙事Ҏ(gu)Q从普通用户用搜索引擎的角度来介l搜索引擎的具体工作程?br>
自顶向下的方法描q搜索引擎执行过E:
用户通过览器提交查询的词或者短?nbsp;PQ搜索引擎根据用L查询q回匚w的网信息列?nbsp;LQ?br>
上述q程涉及C个问题,如何匚w用户的查询以及网信息列表从何而来Q根据什么而排序?用户的查?nbsp;P l过分词器被切割成小词组 <p1,p2 … pn> q被剔除停用?nbsp;( 的、了、啊{字 )Q根据系l维护的一个倒排索引可以查询某个?nbsp;pi 在哪些网中出现q,匚w那些 <p1,p2 … pn> 都出现的|页集即可作为初始结果,更进一步,q回的初始网集通过计算与查询词的相兛_从而得到网|名,?nbsp;Page RankQ按照网늚排名序卛_得到最l的|页列表Q?br>
假设分词器和|页排名的计公式都是既定的Q那么倒排索引以及原始|页集从何而来Q原始网集在之前的数据程的介l中Q可以得知是q?nbsp;spider 爬取|页q且保存在本地的Q而倒排索引Q即词组到网늚映射表是建立在正排烦引的基础上的Q后者是分析了网늚内容q对其内容进行分词后Q得到的|页到词l的映射表,正排烦引倒置卛_得到倒排索引Q?br>
|页的分析具体做什么呢Q由于爬虫收集来的原始网中包含很多信息Q比?nbsp;html 表单以及一些垃圾信息比如广告,|页分析去除q些信息Qƈ抽取其中的正文信息作为后l的基础数据?br>
在有了上q的分析之后Q我们可以得到搜索引擎的整体l构如下图:
?nbsp;2. 搜烦引擎整体l构
爬虫?nbsp;Internet 中爬取众多的|页作ؓ原始|页库存储于本地Q然后网分析器抽取|页中的主题内容交给分词器进行分词,得到的结果用索引器徏立正排和倒排索引Q这样就得到了烦引数据库Q用h询时Q在通过分词器切割输入的查询词组q过索器在烦引数据库中进行查询,得到的结果返回给用户?br>
无论搜烦引擎的规模大,其主要结构都是由q几部分构成的,q没有大的差别,搜烦引擎的好坏主要是军_于各部分的内部实现?br>
有了上述的对与搜索引擎的整体了解Q我们来学习 dySE 中爬虫模块的具体设计和实现?br>
回页?br>
Spider 的设?br>
|页攉的过E如同图的遍历,其中|页׃为图中的节点Q而网中的超链接则作为图中的边,通过某网늚链?nbsp;得到其他|页的地址Q从而可以进一步的q行|页攉Q图的遍历分为广度优先和深度优先两种Ҏ(gu)Q网늚攉q程也是如此。综上,Spider 攉|页的过E如下:从初?nbsp;URL 集合获得目标|页地址Q通过|络q接接收|页数据Q将获得的网|据添加到|页库中q且分析该网中的其?nbsp;URL 链接Q放入未讉K URL 集合用于|页攉。下图表CZq个q程Q?br>
?nbsp;3. Spider 工作程
回页?br>
Spider 的具体实?br>
|页攉?nbsp;Gather
|页攉器通过一?nbsp;URL 来获取该 URL 对应的网|据,其实C要是利用 Java 中的 URLConnection cL打开 URL 对应面的网l连接,然后通过 I/O 读取其中的数据QBufferedReader 提供d数据的缓冲区提高数据d的效率以及其下定义的 readLine() 行读取函数。代码如?nbsp;( 省略了异常处理部?nbsp;)Q?br>
清单 1. |页数据抓取
URL url = new URL(“http://www.xxx.com”);
URLConnection conn = url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = null;
while((line = reader.readLine()) != null)
document.append(line + "\n");
使用 Java 语言的好处是不需要自己处理底层的q接操作Q喜Ƣ或者精?nbsp;Java |络~程的读者也可以不用上述的方法,自己实现 URL cd相关操作Q这也是一U很好的ȝ?br>
|页处理
攉到的单个|页Q需要进行两U不同的处理Q一U是攑օ|页库,作ؓ后箋处理的原始数据;另一U是被分析之后,抽取其中?nbsp;URL q接Q放?nbsp;URL 池等待对应网늚攉?br>
|页的保存需要按照一定的格式Q以便以后数据的扚w处理。这里介l一U存储数据格式,该格式从北大天网的存储格式简化而来Q?br>
|页库由若干记录l成Q每个记录包含一条网|据信息,记录的存放ؓ序dQ?br>
一条记录由数据头、数据、空行组成,序为:头部 + I + 数据 + IQ?br>
头部pq属性组成,有:版本P日期QIP 地址Q数据长度,按照属性名和属性值的方式排列Q中间加冒号Q每个属性占用一行;
数据即ؓ|页数据?br>
需要说明的是,d数据攉日期的原因,׃许多|站的内定w是动态变化的Q比如一些大型门L站的首页内容Q这意味着如果不是当天爬取的网|据,很可能发生数据过期的问题Q所以需要添加日期信息加以识别?br>
URL 的提取分Z步,W一步是 URL 识别Q第二步再进?nbsp;URL 的整理,分两步走主要是因为有些网站的链接是采用相对\径,如果不整理会产生错误。URL 的识别主要是通过正则表达式来匚wQ过E首先设定一个字W串作ؓ匚w的字W串模式Q然后在 Pattern 中编译后卛_使用 Matcher cLq行相应字符串的匚w。实C码如下:
清单 2. URL 识别
public ArrayList<URL> urlDetector(String htmlDoc)
{
final String patternString = "<[a|A]\\s+href=([^>]*\\s*>)";
Pattern pattern = Pattern.compile(patternString,Pattern.CASE_INSENSITIVE);
ArrayList<URL> allURLs = new ArrayList<URL>();
Matcher matcher = pattern.matcher(htmlDoc);
String tempURL;
//初次匚w到的url是Ş如:<a href="http://bbs.life.xxx.com.cn/" target="_blank">
//为此Q需要进行下一步的处理Q把真正的url抽取出来Q?br>
//可以对于前两?之间的部分进行记录得到url
while(matcher.find())
{
try
{
tempURL = matcher.group();
tempURL = tempURL.substring(tempURL.indexOf("\"")+1);
if(!tempURL.contains("\""))
continue;
tempURL = tempURL.substring(0, tempURL.indexOf("\""));
} catch (MalformedURLException e)
{
e.printStackTrace();
}
}
return allURLs;
}
按照“<[a|A]\\s+href=([^>]*\\s*>)”q个正则表达式可以匹配出 URL 所在的整个标签QŞ?#8220;<a href="http://bbs.life.xxx.com.cn/" target="_blank">”Q所以在循环获得整个标签之后Q需要进一步提取出真正?nbsp;URLQ我们可以通过截取标签中前两个引号中间的内Ҏ(gu)获得q段内容。如此之后,我们可以得到一个初步的属于该网늚 URL 集合?br>
接下来我们进行第二步操作QURL 的整理,卛_之前获得的整个页面中 URL 集合q行{选和整合。整合主要是针对|页地址是相寚w接的部分Q由于我们可以很Ҏ(gu)的获得当前网늚 URLQ所以,相对链接只需要在当前|页?nbsp;URL 上添加相寚w接的字段卛_l成完整?nbsp;URLQ从而完成整合。另一斚wQ在面中包含的全面 URL 中,有一些网|如广告网|我们不想爬取的,或者不重要的,q里我们主要针对于页面中的广告进行一个简单处理。一般网站的q告q接都有相应的显C达,比如q接中含?#8220;ad”{表达时Q可以将该链接的优先U降低,q样可以一定程度的避免q告链接的爬取?br>
l过q两步操作时候,可以把该|页的收集到?nbsp;URL 攑օ URL 池中Q接下来我们处理爬虫?nbsp;URL 的派分问题?br>
Dispatcher 分配?br>
分配器管?nbsp;URLQ负责保存着 URL 池ƈ且在 Gather 取得某一个网之后派分新?nbsp;URLQ还要避免网늚重复攉。分配器采用设计模式中的单例模式~码Q负责提供给 Gather 新的 URLQ因为涉及到之后的多U程改写Q所以单例模式显得尤为重要?br>
重复攉是指物理上存在的一个网,在没有更新的前提下,?nbsp;Gather 重复讉KQ造成资源的浪费,主要原因是没有清楚的记录已经讉K?nbsp;URL 而无法L别。所以,Dispatcher l护两个列表 ,“已访问表”Q和“未访问表”。每?nbsp;URL 对应的页面被抓取之后Q该 URL 攑օ已访问表中,而从该页面提取出来的 URL 则放入未讉K表中Q当 Gather ?nbsp;Dispatcher h URL 的时候,先验证该 URL 是否在已讉K表中Q然后再l?nbsp;Gather q行作业?br>
Spider 启动多个 Gather U程
现在 Internet 中的|页数量C亿计Q而单独的一?nbsp;Gather 来进行网|集显然效率不I所以我们需要利用多U程的方法来提高效率。Gather 的功能是攉|页Q我们可以通过 Spider cL开启多?nbsp;Gather U程Q从而达到多U程的目的。代码如下:
/** *//**
* 启动U程 gatherQ然后开始收集网资?br>
*/
public void start()
{
Dispatcher disp = Dispatcher.getInstance();
for(int i = 0; i < gatherNum; i++)
{
Thread gather = new Thread(new Gather(disp));
gather.start();
}
}
在开启线E之后,|页攉器开始作业的q作Qƈ在一个作业完成之后,?nbsp;Dispatcher 甌下一个作业,因ؓ有了多线E的 GatherQؓ了避免线E不安全Q需要对 Dispatcher q行互斥讉KQ在其函C中添?nbsp;synchronized 关键词,从而达到线E的安全讉K?br>
回页?br>
结
Spider 是整个搜索引擎的基础Qؓ后箋的操作提供原始网资料,所以了?nbsp;Spider 的编写以及网库的组成结构ؓ后箋预处理模块打下基。同?nbsp;Spider E加修改之后也可以单独用于某cd体信息的搜集Q比如某个网站的囄爬取{?br>
回页?br>
后箋内容
在本pd的第 2 部分中,(zhn)将了解到爬虫获取的|页库如何被预处理模块逐步提取内容信息Q通过分词q徏成倒排索引Q而在W?nbsp;3 部分中,(zhn)将了解刎ͼ如何~写|页来提供查询服务,q且如何昄的返回的l果和完成快照的功能?/span>
很多资料上提供的的下载地址是:http://www.sysdeo.com/eclipse/tomcatPlugn 恼火的是Q这个网址已经指向www.sqli.comQ因为外语不好,也找不到下蝲的地斏V?/p>
在搜索tomcatPluginV32 下蝲Q找到的是CSDN上的Q最讨厌CSDN上下载开源的东西q要登陆Q还要消耗积分,其他的大多也上面的不能用的连接?/p>
后来没办法,只搜索tomcatPlugin扑ֈ了官|:http://www.eclipsetotale.com/tomcatPlugin.html
也找C官方的下载地址Q?a >http://www.eclipsetotale.com/tomcatPlugin/tomcatPluginV321.zip
1.下蝲heritrix-1.14.1.zip和heritrix-1.14.1.src q解压,解压heritrix-1.14.1.jar. 2.在eclipse下创建java project,命名为比如heritrixQ进入其工程的目录,我的是F:\workspace\myeclipse\heritrixQ删除src文g夏V? 3.copy解压后的heritrix-1.14.1.zip文g夹下的libQwebappsQheritrix-1.14.1到F:\workspace\myeclipse\heritrix目录下,q删除F:\workspace\myeclipse\heritrix\heritrix-1.14.1目录下的org和st两个文g夏V? copy解压后的heritrix-1.14.1.src 文g夹下的heritrix-1.14.1\src\java下的org和st两个文g夹到F:\workspace\myeclipse\heritrix\heritrix-1.14.1\目录? 4.修改heritrix-1.14.1文g夹名UCؓsrc 5.修改src\heritrix.properties文g中的heritrix.cmdline.admin = ?heritrix.cmdline.admin = admin:sun,q个是要设|你的用户名和密码,可以随便Q中间是冒号? 6.h工程Q把lib下的jar包全部添加到工程中,即点击heritrix工程Q右键属?--java build path---libraries--- add jars选择heritrix工程下lib所有jar? 7.q行org.archive.crawler.Heritrixc,在地址栏输?a style="COLOR: rgb(38,28,220)" href="http://localhost:8080/" target=_blank>http://localhost:8080/ OK!是q么单!?/span>自:http://zhidao.baidu.com/question/72080439.html
Spider即网l爬?,其定义有q义和狭义之分。狭义上指遵循标准的 http协议利用链接和 Web文档索的Ҏ(gu)遍历万维|信息空间的软gE序 ;而广义的定义则是所有能遵@ http协议?Web文档的Y仉UC为网l爬虫?
Spider是一个功能很强的自动提取|页的程?,它ؓ搜烦引擎从万l网上下载网?,是搜索引擎的重要l成 .它通过h站点上的 HTML文档讉K某一站点。它遍历 WebI间 ,不断从一个站点移动到另一个站?,自动建立索引 ,q加入到|页数据库中。网l爬虫进入某个超U文本时 ,它利?HTML语言的标记结构来搜烦信息及获取指向其他超U文本的 URL地址 ,可以完全不依赖用户干预实现网l上的自动爬行和搜烦?
Q?Q等待队?:新发现的 URL被加入到q个队列 ,{待?SpiderE序处理 ;
Q?Q处理队?:要被处理?URL被传送到q个队列。ؓ了避免同一?URL被多ơ处?,当一?URL被处理过?,它将被{Ud完成队列或者错误队?(如果发生错误 )?
Q?Q错误队?:如果在下载网|发生错误 ,?URL被加入 到错误队列?/p>
Q?Q完成队?:如果在处理网|有发生错?,?URL被加入到完成队列?
在抓取网늚时?,目前|络爬虫一般有两种{略 :无主题搜索与Z某特定主体的专业搜烦。其中前者主要包?:q度优先和深度优先。广度优先是指网l爬虫会先抓取v始网中链接的所有网?,然后再选择其中的一个链接网?,l箋抓取在此|页中链接的所有网c这是最常用的方?因ؓq个Ҏ(gu)可以让网l爬虫ƈ行处?,提高其抓取速度。深度优先是指网l爬虫会从v始页开?,一个链接一个链接跟t下?,处理完这条线路之后再转入下一个v始页 ,l箋跟踪链接。这个方法有个优Ҏ(gu)|络爬虫在设计的时候比较容易。大多数|页爬行器采用宽度优先搜索策略或者是对这U策略的某些改进?/p>
在专业搜索引擎中 ,|络爬虫的Q务是获取 Web面和决定链接的讉K序 ,它通常从一?“U子?”(如用h询、种子链接或U子面 )?以P代的方式讉K面和提取链接。搜索过E中 ,未访问的链接被暂存在一个称?“搜烦前沿 ”(Spider Frontier)的队列中 ,|络爬虫Ҏ(gu)搜烦前沿中链接的 “重要E度 ”军_下一个要讉K的链接。如何评价和预测链接?“重要E度 ”(或称价?)是决定网l爬虫搜索策略的关键?/p>
众多的网l爬虫设计各不相?,但归根结底是采用不同的链接h(hun)Dh准?/p>
开发语aQJava
http://lucene.apache.org/nutch/
介:
Apache的子目之一Q属于Lucene目下的子项目?/p>
Nutch是一个基于LuceneQ类似Google的完整网l搜索引擎解x案,ZHadoop的分布式处理模型保证了系l的性能Q类似Eclipse的插件机制保证了pȝ的可客户化,而且很容易集成到自己的应用之中?
开发语aQC++
http://larbin.sourceforge.net/index-eng.html
?/p>
larbin是一U开源的|络爬虫/|络蜘蛛Q由法国的年Mh Sébastien Ailleret独立开发。larbin目的是能够跟t页面的urlq行扩展的抓取,最后ؓ搜烦引擎提供q泛的数据来源?/p>
Larbin只是一个爬虫,也就是说larbin只抓取网,至于如何parse的事情则q戯己完成。另外,如何存储到数据库以及建立索引的事?larbin也不提供?/p>
latbin最初的设计也是依据设计单但是高度可配置性的原则Q因此我们可以看刎ͼ一个简单的larbin的爬虫可以每天获取5Q0万的|页Q非帔R效?/p>
开发语aQJava
?/p>
与Nutch比较
?Nutch。二者均为Java开源框ӞHeritrix ?SourceForge上的开源品,Nutch为Apache的一个子目Q它们都UC|络爬虫/蜘蛛Q?Web CrawlerQ,它们实现的原理基本一_深度遍历|站的资源,这些资源抓取到本地Q用的Ҏ(gu)都是分析|站每一个有效的URIQƈ提交HttphQ从而获得相应结果,生成本地文g及相应的日志信息{?/p>
Heritrix 是个 "archival crawler" -- 用来获取完整的、精的、站点内容的深度复制。包括获取图像以及其他非文本内容。抓取ƈ存储相关的内宏V对内容来者不拒,不对面q行内容上的修改。重新爬行对相同的URL不针对先前的q行替换。爬虫通过Web用户界面启动、监控、调_允许Ҏ(gu)的定义要获取的URL?/p>
二者的差异Q?/p>
Nutch 只获取ƈ保存可烦引的内容。Heritrix则是照单全收。力求保存页面原?
Nutch 可以修剪内容Q或者对内容格式q行转换?
Nutch 保存内容为数据库优化格式便于以后索引Q刷新替换旧的内宏V而Heritrix 是添?q加)新的内容?
Nutch 从命令行q行、控制。Heritrix ?Web 控制理界面?
Nutch 的定制能力不够强Q不q现在已l有了一定改q。Heritrix 可控制的参数更多?/p>
Heritrix提供的功能没有nutch多,有点整站下蝲的味道。既没有索引又没有解析,甚至对于重复爬取URL都处理不是很好?/p>
Heritrix的功能强?但是配置h却有炚w烦?/p>
一、从功能斚w来说QHeritrix与Larbin的功能类伹{都是一个纯_的|络爬虫Q提供网站的镜像下蝲。而Nutch是一个网l搜索引擎框Ӟ爬取|页只是其功能的一部分?/p>
二、从分布式处理来_Nutch支持分布式处理,而另外两个好像尚且还没有支持?/p>
三、从爬取的网存储方式来_Heritrix?Larbin都是爬取下来的内容保存为原始类型的内容。而Nutch是将内容保存到其特定格式的segment中去?/p>
四,对于爬取下来的内容的处理来说QHeritrix?Larbin都是爬取下来的内容不经处理直接保存为原始内宏V而NutchҎ(gu)本进行了包括链接分析、正文提取、徏立烦引(Lucene索引Q等处理?/p>
五,从爬取的效率来说QLarbin效率较高Q因为其是用c++实现的ƈ且功能单一?/p>
?3U爬虫的比较
crawler |
开发语a |
功能单一 |
支持分布式爬?/p> |
效率 |
镜像保存 |
Nutch |
Java |
× |
√ |
?/p> |
× |
Larbin |
C++ |
√ |
× |
?/p> |
√ |
Heritrix |
Java |
√ |
× |
?/p> |
√ |
Heritrix
Heritrix是一个开源,可扩展的web爬虫目。Heritrix设计成严格按照robots.txt文g的排除指C和META robots标签?br>http://crawler.archive.org/
WebSPHINX
WebSPHINX是一个Javacd和Web爬虫的交互式开发环境。Web爬虫(也叫作机器h或蜘?是可以自动浏览与处理Web面的程序。WebSPHINX׃部分l成Q爬虫工作^台和WebSPHINXcd?br>http://www.cs.cmu.edu/~rcm/websphinx/
WebLech
WebLech是一个功能强大的Web站点下蝲与镜像工兗它支持按功能需求来下蝲web站点q能够尽可能模仿标准Web览器的行ؓ。WebLech有一个功能控制台q用多U程操作?br>http://weblech.sourceforge.net/
Arale
Arale主要ZZ用而设计,而没有像其它爬虫一hx于页面烦引。Arale能够下蝲整个web站点或来自web站点的某些资源。Araleq能够把动态页面映成静态页面?br>http://web.tiscali.it/_flat/arale.jsp.html
J-Spider
J-Spider:是一个完全可配置和定制的Web Spider引擎.你可以利用它来检查网站的错误(内在的服务器错误{?,|站内外部链接检查,分析|站的结?可创Z个网站地?,下蝲整个Web站点Q你q可以写一个JSpider插g来扩展你所需要的功能?br>http://j-spider.sourceforge.net/
spindle
spindle 是一个构建在Lucene工具包之上的Web索引/搜烦工具.它包括一个用于创建烦引的HTTP spider和一个用于搜索这些烦引的搜烦cRspindle目提供了一lJSP标签库得那些基于JSP的站点不需要开发Q何Javacd能够增加搜烦功能?br>http://www.bitmechanic.com/projects/spindle/
Arachnid
Arachnid: 是一个基于Java的web spider框架.它包含一个简单的HTML剖析器能够分析包含HTML内容的输入流.通过实现Arachnid的子cd能够开发一个简单的Web spidersq能够在Web站上的每个页面被解析之后增加几行代码调用?Arachnid的下载包中包含两个spider应用E序例子用于演示如何使用该框架?br>http://arachnid.sourceforge.net/
LARM
LARM能够为Jakarta Lucene搜烦引擎框架的用h供一个纯Java的搜索解x案。它包含能够为文Ӟ数据库表格徏立烦引的Ҏ(gu)和ؓWeb站点建烦引的爬虫?br>http://larm.sourceforge.net/
JoBo
JoBo 是一个用于下载整个Web站点的简单工兗它本质是一个Web Spider。与其它下蝲工具相比较它的主要优势是能够自动填充f(xi)orm(如:自动d)和用cookies来处理session。JoBoq有灉|的下载规?如:通过|页的URLQ大,MIMEcd{?来限制下载?br>http://www.matuschek.net/software/jobo/index.html
snoics-reptile
snoics -reptile是用UJava开发的Q用来进行网站镜像抓取的工具Q可以用配制文件中提供的URL入口Q把q个|站所有的能用览器通过GET的方式获取到的资源全部抓取到本地Q包括网和各种cd的文Ӟ如:囄、flash、mp3、zip、rar、exe{文件。可以将整个|站完整C传至盘内,q能保持原有的网站结构精不变。只需要把抓取下来的网站放到web服务?如:Apache)中,可以实现完整的|站镜像?br>http://www.blogjava.net/snoics
Web-Harvest
Web-Harvest是一个Java开源Web数据抽取工具。它能够攉指定的Web面q从q些面中提取有用的数据。Web-Harvest主要是运用了像XSLT,XQuery,正则表达式等q些技术来实现对text/xml的操作?br>http://web-harvest.sourceforge.net
spiderpy
spiderpy是一个基于Python~码的一个开源web爬虫工具Q允许用h集文件和搜烦|站Qƈ有一个可配置的界面?br>http://pyspider.sourceforge.net/
The Spider Web Network Xoops Mod Team
pider Web Network Xoops Mod是一个Xoops下的模块Q完全由PHP语言实现?br>http://www.tswn.com/
larbin
larbin是个ZC++的web爬虫工具Q拥有易于操作的界面Q不q只能跑在LINUX下,在一台普通PC下larbin每天可以?百万个页?当然啦,需要拥有良好的|络)
http://larbin.sourceforge.net/index-eng.html
1. robots.txt
robots.txt是一个纯文本文gQ在q个文g中网站管理者可以声明该|站中不惌robots讉K的部分,或者指定搜索引擎只收录指定的内宏V?/p>
当一个搜索机器hQ有的叫搜烦蜘蛛Q访问一个站Ҏ(gu)Q它会首先检查该站点根目录下是否存在robots.txtQ如果存在,搜烦机器人就会按照该文g中的内容来确定访问的范围Q如果该文g不存在,那么搜烦机器人就沿着链接抓取?/p>
另外Qrobots.txt必须攄在一个站点的根目录下Q而且文g名必d部小写?/p>
2. 有些cd的网难以爬取。例如,使用javascript调用的页面、需要注册才能访问的面{?/p>
有些cd的网难以爬取。例如,使用javascript调用的页面、需要注册才能访问的面{,对于q些|络的爬取被归结为深层网l的挖掘。这些网可归结为如下几c:Q?Q通过
填写表单形成对后台再现数据库查询得到的动态页面。(2Q由于缺乏被指向的超链接而没有被索引到的面。(3Q需要注册或其他限制讉K的页面。(4Q可讉K的非|页文g。在曾伟辉等人的文章中,对这c问题进行了lD。在王映{h的文章中Q提Z使用一个嵌入式的JavaScript引擎来进行动态网采集的Ҏ(gu)?/p>
1. 有些非静态的Web2.0|站的内容动态生成,数据量巨大,难以抓取Q例如论坛等|站。在2008qSIGIR中,Yida Wang{提Z一U爬取论坛的爬取Ҏ(gu)?/p>
2. 有些|站会限制网l爬虫的爬取QAnalia G. Lourenco, Orlando O. Belo ?006q提出来使用查询日志的方法限制网l爬虫的zd以减L务器压力?/p>
3. |络上的|页数量太大Q在爬取旉要考虑爬取的时间及效率{问题,UCLA的Junghoo Cho{提Z使用q行的crawler的方法?/p>
4.