l 打開PanGu4Lucene\WebDemo\Bin,將Dictionaries添加到項目根路徑(改名為Dict),添加對PanGu.dll(同目錄下不要有Pangu.xml,那個默認的配置文件的選項對於分詞結果有很多無用信息)、PanGu.Lucene.Analyzer.dll的引用
l 把上節代碼的Analyzer用PanGuAnalyzer代替
l 運行報錯?通用技巧:把Dict目錄下的文件“復制到輸出目錄”設定為“如果較新則復制”。
對以下引用進行添加:
使用代碼示例:
Analyzer analyzer = new PanGuAnalyzer(); //Analyzer analyzer = new StandardAnalyzer();//new后面的類為分詞的方法 TokenStream tokenStream = analyzer.TokenStream("", new System.IO.StringReader(TextBox1.Text)); Lucene.Net.Analysis.Token token = null; while ((token = tokenStream.Next()) != null) { //Console.WriteLine(token.TermText()); Response.Write(token.TermText()+"<br/>"); }
Lucene.Net核心類簡介
l Directory表示索引文件(Lucene.net用來保存用戶扔過來的數據的地方)保存的地方,是抽象類,兩個子類FSDirectory(文件中)、RAMDirectory (內存中)。使用的時候別和IO里的Directory弄混了。
l 創建FSDirectory的方法,FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath),new NativeFSLockFactory()), path索引的文件夾路徑
l IndexReader對索引進行讀取的類,對IndexWriter進行寫的類。
l IndexReader的靜態方法bool IndexExists(Directory directory)判斷目錄directory是否是一個索引目錄。IndexWriter的bool IsLocked(Directory directory) 判斷目錄是否鎖定,在對目錄寫之前會先把目錄鎖定。兩個IndexWriter沒法同時寫一個索引文件。IndexWriter在進行寫操作的時候會自動加鎖,close的時候會自動解鎖。IndexWriter.Unlock方法手動解鎖(比如還沒來得及close IndexWriter 程序就崩潰了,可能造成一直被鎖定)。
創建索引:
l 構造函數:IndexWriter(Directory dir, Analyzer a, bool create, MaxFieldLength mfl)因為IndexWriter把輸入寫入索引的時候,Lucene.net是把寫入的文件用指定的分詞器將文章分詞(這樣檢索的時候才能查的快),然后將詞放入索引文件。
l void AddDocument(Document doc),向索引中添加文檔(Insert)。Document類代表要索引的文檔(文章),最重要的方法Add(Field field),向文檔中添加字段。Document是一片文檔,Field是字段(屬性)。Document相當於一條記錄,Field相當於字段。
l Field類的構造函數 Field(string name, string value, Field.Store store, Field.Index index, Field.TermVector termVector):
l name表示字段名; value表示字段值;
l store表示是否存儲value值,可選值 Field.Store.YES存儲, Field.Store.NO不存儲, Field.Store.COMPRESS壓縮存儲;默認只保存分詞以后的一堆詞,而不保存分詞之前的內容,搜索的時候無法根據分詞后的東西還原原文,因此如果要顯示原文(比如文章正文)則需要設置存儲。
l index表示如何創建索引,可選值Field.Index. NOT_ANALYZED
l ,不創建索引,Field.Index. ANALYZED,創建索引;創建索引的字段才可以比較好的檢索。是否碎屍萬段!是否需要按照這個字段進行“全文檢索”。
l termVector表示如何保存索引詞之間的距離。“北京歡迎你們大家”,索引中是如何保存“北京”和“大家”之間“隔多少單詞”。方便只檢索在一定距離之內的詞。
l 為什么要把帖子的url做為一個Field,因為要在搜索展示的時候先帖子地址取出來構建超鏈接,所以Field.Store.YES;一般不需要對url進行檢索,所以Field.Index.NOT_ANALYZED
l 案例:對1000至1100號帖子進行索引。“只要能看懂例子和文檔,稍作修改即可實現自己的需求”
1、對數據進行索引
string indexPath = "c:/temp"; FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath),new NativeFSLockFactory()); bool isUpdate = IndexReader.IndexExists(directory); if (isUpdate) { //如果索引目錄被鎖定(比如索引過程中程序異常退出),則首先解鎖 if (IndexWriter.IsLocked(directory)) { IndexWriter.Unlock(directory); } } IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); for (int i = 1000; i < 1100; i++) { string txt = File.ReadAllText(@"C:\MxDownload\dnt_3.1.0_sqlserver\upload_files\文章\" + i + ".txt"); Document document = new Document(); document.Add(new Field("number", i.ToString(), Field.Store.YES, Field.Index. NOT_ANALYZED)); document.Add(new Field("body", txt, Field.Store.YES, Field.Index. ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); writer.AddDocument(document); Console.WriteLine("索引"+i+"完畢"); } writer.Close(); directory.Close();//不要忘了Close,否則索引結果搜不到
搜索:
l IndexSearcher是進行搜索的類,構造函數傳遞一個IndexReader。IndexSearcher的void Search(Query query, Filter filter, Collector results)方法用來搜索,Query是查詢條件, filter目前傳遞null, results是檢索結果,TopScoreDocCollector.create(1000, true)方法創建一個Collector,1000表示最多結果條數,Collector就是一個結果收集器。
l Query有很多子類,PhraseQuery是一個子類。 PhraseQuery用來進行多個關鍵詞的檢索,調用Add方法添加關鍵詞,query.Add(new Term("字段名", 關鍵詞)),PhraseQuery. SetSlop(int slop)用來設置關鍵詞之間的最大距離,默認是0,設置了Slop以后哪怕文檔中兩個關鍵詞之間沒有緊挨着也能找到。
• query.Add(new Term("字段名", 關鍵詞))
• query.Add(new Term("字段名", 關鍵詞2))
• 類似於:where 字段名 contains 關鍵詞 and 字段名 contais 關鍵詞2
l 調用TopScoreDocCollector的GetTotalHits()方法得到搜索結果條數,調用Hits的TopDocs TopDocs(int start, int howMany)得到一個范圍內的結果(分頁),TopDocs的scoreDocs字段是結果ScoreDoc數組, ScoreDoc 的doc字段為Lucene.Net為文檔分配的id(為降低內存占用,只先返回文檔id),根據這個id調用searcher的Doc方法就能拿到Document了(放進去的是Document,取出來的也是Document);調用doc.Get("字段名")可以得到文檔指定字段的值,注意只有Store.YES的字段才能得到,因為Store.NO的沒有保存全部內容,只保存了分割后的詞。
l 編寫檢索功能,搜索“網站 志願者”。練習分詞,用戶不用空格。如果確定用盤古分詞,那么用盤古的Segment類更方便。
l 檢索不出來的可能的原因:路徑問題,分詞是否正確、盤古分詞如果指定忽略大小寫,則需要統一按照小寫進行搜索
l Todo:第一個版本應該保存body和title,搜索結果形成超鏈接,不顯示正文。
搜索代碼:
string kw = Console.ReadLine(); FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory()); IndexReader reader = IndexReader.Open(directory,true); IndexSearcher searcher = new IndexSearcher(reader); PhraseQuery query = new PhraseQuery(); foreach (string word in kw.Split(' '))//先用空格,讓用戶去分詞,空格分隔的就是詞“計算機 專業” { query.Add(new Term("body", word)); } query.SetSlop(100); TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true); searcher.Search(query, null, collector); ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs; for (int i = 0; i < docs.Length; i++) { int docId = docs[i].doc; Document doc = searcher.Doc(docId); Console.WriteLine(doc.Get("number")); Console.WriteLine(doc.Get("body")); }
網頁采集:
l 復習WebClient的用法,調用DownloadString方法下載頁面。抓取DiscuzNT!的900至1000貼。亂碼怎么辦?亂碼的唯一原因“編碼不一致”。
l WebClient抓取到的是頁面的源代碼,如何得到頁面的標題、文字、超鏈接呢?
l 用mshtml進行html的解析,IE就是使用mshtml進行網頁解析的。添加對Microsoft.mshtml的引用(如果是VS2010,修改這個引用的“嵌入互操作類型”為False。(*)“復制本地”設置為True、“特定版本”設置為False,這樣在沒有安裝VS的機器中也可以用。)
• HTMLDocumentClass doc = new HTMLDocumentClass();
• doc.designMode = "on"; //不讓解析引擎去嘗試運行javascript
• doc.IHTMLDocument2_write(要解析的代碼);
• doc.title、doc.body.innerText,更多用法自己探索。
• 所有Dom方法都能在mshtml中調用
搜索結果高亮顯示的方法:
l 高亮顯示,只顯示包含關鍵詞的部分。參考盤古分詞的文檔。
l 從網上、文檔找來的代碼不用細讀每行代碼,先把它拿過來運行通過再說。
l 不用每次改代碼都重啟,在項目的屬性頁面的Web中選中“啟用編輯並繼續(Enable Edit and Continue)”
高亮顯示的代碼:
private static String highLight(string keyword,String content) { PanGu.HighLight.SimpleHTMLFormatter formatter = new PanGu.HighLight.SimpleHTMLFormatter("<font color='red'>", "</font>"); PanGu.HighLight.Highlighter highlighter = new PanGu.HighLight.Highlighter(formatter, new Segment()); highlighter.FragmentSize = 500; string msg = highlighter.GetBestFragment(keyword,content); if (string.IsNullOrEmpty(msg)) { return content; } else { return msg; } } String hightlightTitle = highLight(keyword, title); String hightlightBody = HttpUtility.HtmlEncode(body);//防止XSS攻擊 hightlightBody = highLight(keyword, hightlightBody);