成搜索引擎從 1.網頁下載, 2.文本分析, 3.索引生成, 4.索引存儲, 5.信息檢索 等各個層面的應用。
一個搜索引擎的好壞的評價標準: 1.相關性 2.數據量 3.查全率 4.相應速度 5.更新速度 互聯網搜索引擎的五個主要環節,系統主要模塊包括 1.網頁信息的抓取; ?? 1.深度優先搜集策略 ?? 2.IP段掃描搜索策略 ?? 3.廣度優先搜集策略 2.網頁內容的分析; ?? 1.分詞 ?? 2.過濾 ?? 3.轉換 3.網頁索引的建立; ?? 1.索引器是用來建立索引的軟件和程序; ?? 2.索引模型:倒排文檔,矢量空間模型,概率模型等,一般索引表采用倒排索引。 4.網頁檢索結果排序; 5.網頁檢索工具與接口。 用到的java api: java.net.*; java.net.www.html; 提供了處理HTML語言的功能方法, 而Java.net.www.http;提供了處理HTTP協議的功能方法。 ? 實例解析: 第一步:數據的抓取 網絡采集程序的大致設計思路: ?? 1.先確定需要下載的網頁的URL,指定通信端口,創建一個用于網絡通信的 ?? Socket對象,網頁下載默認端口是80, ?? 2.結果通過流式輸出接口輸出,創建相應的輸出對象。通過輸入接口,向Socket中 ?? 傳入HTTP下載請求。 ?? 3.遠端的目標Web服務器得到請求后,發送應答消息。本地Socket對象收到消息后緩沖并輸出,就完成整個網頁的下載功能。 實踐: ?? 完成一個簡單的網頁下載,并保存到本地的功能程序。WebCrawlerMe.java如下: package chapter2; import java.io.BufferedReader; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; /** * @author gf * */ public class WebCrawlerMe { ??? /** ???? * @param args ???? */ ??? public static void main(String[] args) { ??????? try { ??????????? ??????????? PrintWriter pw = new PrintWriter(new FileOutputStream("D:\\aa.jsp")); ??????????? OutputStream out = new FileOutputStream("D:\\aa.jsp"); ??????????? //生成下載對象 ??????????? Socket webclient = new Socket("www.bnu.edu.cn", 80); ??????????? //輸出流 ??????????? PrintWriter result = new PrintWriter(webclient.getOutputStream(), ??????????????????? true); ??????????? //輸入流 ??????????? BufferedReader receiver = new BufferedReader(new InputStreamReader( ??????????????????? webclient.getInputStream())); ??????????? //發送Http request請求 ??????????? result.println("GET / HTTP/1.1"); ??????????? result.println("Host:bnu.edu.cn"); ??????????? result.println("Connection:close"); ??????????? result.println(); ??????????? //result.println("=============================="); ??????????? //接收HTTP Response 返回結果信息 ??????????? boolean bRet = true; ??????????? StringBuffer sb = new StringBuffer(8096); ??????????? while (bRet) { ??????????????? if (receiver.ready()) { //判斷此流是否已準備好被讀取。 ??????????????????? int idx = 0; ??????????????????? while (idx != -1) { ??????????????????????? idx = receiver.read(); //讀取單個字符 ??????????????????????? sb.append((char) idx); ??????????????????? } ??????????????????? bRet = false; ??????????????? } ??????????? } ??????????? //顯示獲得的網頁正文,打印到控制臺 ??????????? System.out.println(sb.toString()); ??????????? pw.print(sb.toString()); ??????????? webclient.close(); ??????????? pw.close(); ??????? } catch (UnknownHostException e) { ??????????? System.err.println("無法訪問指定主機"); ??????????? e.printStackTrace(); ??????? } catch (IOException e) { ??????????? System.err.println("下載失敗,請檢查輸入地址是否正確"); ??????????? e.printStackTrace(); ??????? } ??? } } 第二步:網頁分析程序實現 實現原理: ?? 分析方法包括格式化字符去除,文本正文分詞,信息過濾等基本任務 ?? 1.簡單語言標記去除方法主要根據HTML語言的特點實現; ?? 2.正則表達式信息抽取主要利用模板方式提取有效信息; ?? 3.DOM樹內容抽取是利用每個網頁都有一定的層次結構的特點。 系統結構: 原始HTML文檔---->網頁結構化分析 ----> 網頁結構信息 ?????????? ----> 視覺模塊分析?? ----> 視覺特征信息 ----> 信息過濾 ????????? ----> 去除HTML標記?? ----> 網頁文本信息 實踐: ?? 1.完成一個簡單的去除網頁中的<>標簽功能程序。WebParser.java,如下: ?? 2.完成一個簡單的簡單字分割,過濾各種標點符號,結果文字以空格來分割不同漢字和英文詞組。 package chapter2; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /** * @author gf * */ public class WebParser { ??? ??? private static String src_File_Path= "D:\\workshop\\htmlsrc.html"; ??? private static String dst_File_Path= "D:\\workshop\\htmlsrc.txt"; ??? /** ???? * @param args ???? */ ??? public static void main(String[] args) { ??????? try { ??????????? parser(); ??????? } catch (IOException e) { ??????????? // TODO Auto-generated catch block ??????????? e.printStackTrace(); ??????? } ??????? ??? } ??? ??? public static void parser() throws IOException{ ??????? ??????? //標志位,用來標志是否進入<>內部,當為FALSE時,進入標簽內部 ??????? //當位true時,進入<>外部 ??????? boolean bContent = true; ??????? ??????? /**Constructs a string buffer with no characters in it, ??????? *The initial capacity of the string buffer is 8096*2 ??????? * */ ??????? StringBuffer sBuffer = new StringBuffer(8096*2); ??????? ??????? char [] cBuffer = new char[8096*2]; ??????? int nCount = 0; ??????? ??????? //準備讀寫文件 ??????? File srcFile = new File(src_File_Path); ??????? FileReader fileReader = new FileReader(srcFile); ??????? File dstFile = new File(dst_File_Path); ??????? FileWriter fileWriter = new FileWriter(dstFile); ??????? //返回讀取的字符的個數 ??????? nCount = fileReader.read(cBuffer); ??????? ??????? //開始處理文本正文 ??????? for(int i = 0;i < nCount;i++){ ??????????? ??????????? if(bContent == false){ ??????????????? if(cBuffer[i] == '>'){ ??????????????????? bContent = true; ??????????????? }else{ ??????????????????? continue; ??????????????? } ??????????? }else{ ??????????????? if(cBuffer[i] == '<'){ ??????????????????? bContent = false; ??????????????????? continue; ??????????????? }else if(cBuffer[i] == '\n' || cBuffer[i] == ' '){ //過濾 ??????????????????? continue; ??????????????? }else if(cBuffer[i] == '&' && cBuffer[i+1] =='n' &&??? //過濾  ??????????????????? cBuffer[i+2] == 'b' && cBuffer[i+3] =='s' && cBuffer[i+4] =='p'){ ??????????????????? i= i+5; ??????????????????? continue; ??????????????? } ??????????????? sBuffer.append(cBuffer[i]); ??????????????? fileWriter.write(cBuffer[i]); ??????????? } ??????? }?????? ??????? System.out.println(sBuffer.toString()); ?????? // fileWriter.write(sBuffer.toString()); ??????? fileWriter.close(); ??????? fileReader.close(); ??????? ??? } } 第三步:網頁索引程序實現 實現原理: ?? 采用關鍵詞匹配,核心算法采用倒排索引結構進行。 ?? 倒排索引是一種以關鍵詞作為索引關鍵字和鏈表訪問入口的索引結構。通常保存在內存中,提高訪問速度, ?? 利用索引關鍵字直接確定文檔列表,最后確定希望找到的文檔列表。由于搜索引擎中的文件通常并不單獨存放, ?? 而是存儲在一個巨型的文件庫里。為了節約內存,文檔在索引中通常以文件庫編號以及文件偏移量來代表。 ?? 當通過關鍵字檢索時,檢索的結果可以直接通過位置信息計算得到。在需要的時候可以讀取內存空間或磁盤文件得到。 網頁索引程序設計 ?? 程序的基本思想是采用文檔關鍵字作為索引,生產按照關鍵字組合的鏈表,每個鏈表都是包含了特定關鍵字的文檔集合。 ?? 在檢索過程中按照關鍵字的哈希值或其它的映射算法,快速定位關鍵字鏈表。在得到的文檔集合基礎上進行排序和過濾,就可以快速 ?? 得到需要的文檔集合。 ?? 整個程序以兩個循環為主線,對每個文檔中的每個語素進行處理。每個語素作為關鍵字生成一個Hash值,并把附加的相關文檔信息作為索引項保存。 ?? 每個關鍵字的索引項需要添加到Hash表中,但必須先檢查是否已經存在相同的關鍵字的索引項。 ?? 如果已經存在相同的關鍵字,需要取出相關關鍵字,并鏈接到當前的索引項之后,形成每個關鍵字的關聯文檔集合鏈表。 實踐: ?? 1.索引程序包括2部分:一個是索引項類代碼,包括了對索引項維護以及屬性的操作;InfoItem.java ?????????????????? 一個是索引創建類,完成索引的創建工作。WordIndex.java
package chapter2.index; /** * @author gf * @date 2008-10-16 */ //function: 索引項類,包括存儲位置和后續指針成員變量 // 一系列參數設置和獲取的接口 public class InfoItem { ??? public int fileID; //文件編號 ??? public int offset; //文件偏移 ??? InfoItem next; //后續節點指針 ??? ??? /** ???? * 默認構造函數 ???? */ ??? public InfoItem() { ??????? super(); ??????? // TODO Auto-generated constructor stub ??? } ??? ??? /** ???? * @param fileID ???? * @param offset ???? * 參數初始化 ???? */ ??? public InfoItem(int fileID, int offset) { ??????? super(); ??????? this.fileID = fileID; ??????? this.offset = offset; ??????? this.next = null; ??? } ??? /** ???? * @return the fileID ???? */ ??? public final int getFileID() { ??????? return fileID; ??? } ??? /** ???? * @param fileID the fileID to set ???? */ ??? public final void setFileID(int fileID) { ??????? this.fileID = fileID; ??? } ??? /** ???? * @return the next ???? */ ??? public final InfoItem getNext() { ??????? return next; ??? } ??? /** ???? * @param next the next to set ???? */ ??? public final void setNext(InfoItem next) { ??????? this.next = next; ??? } ??? /** ???? * @return the offset ???? */ ??? public final int getOffset() { ??????? return offset; ??? } ??? /** ???? * @param offset the offset to set ???? */ ??? public final void setOffset(int offset) { ??????? this.offset = offset; ??? } }
package chapter2.index; import java.util.Hashtable; /** * @author gf * */ public class WordIndex { ??? ??? private static Hashtable keyWordIdx; //關鍵詞哈希表 ??? //存放原始文件內容,每一數組代表一個文件 ??? private static String [] FileList = {"北京師范大學", ??????? "北師大附屬試驗小學","北師大第二附屬中學"}; ??? ??? //根據給定的內容,按照漢字建立Hash索引 ??? public static void index() throws Exception{ ??????? InfoItem item1,item2; ??????? System.out.println("index:===========begin=========="); ??????? keyWordIdx = new Hashtable(); //創建關鍵詞Hash表 ??????? ??????? //關鍵詞初始長度 ??????? System.out.println("index:Hashtable initial Size:"+keyWordIdx.size()); ??????? ??????? for(int i=0;i<3;i++){ ??????????? int len = FileList[i].length(); ??????????? for(int j =0;j<len;j++){ ??????????????? item1 = new InfoItem(i,i); //生產文件存放位置的附屬信息 ??????????????? String key = FileList[i].substring(j,j+1); ??????????????? System.out.print(key); ??????????????? if(!keyWordIdx.containsKey(key)){ ??????????????????? keyWordIdx.put(key, item1); ??????????????? }else{ ??????????????????? //提取原始的存儲項 ??????????????????? item2 = (InfoItem) keyWordIdx.get(key); ??????????????????? item1.setNext(item2); //新文件節點添加到鏈表頭 ??????????????????? keyWordIdx.put(key, item1);//添加關鍵字索引到Hash表 ??????????????? } ??????????? } ??????? } ??????? System.out.println("\nindex:Hashtable finish Size:"+keyWordIdx.size()); ??????? System.out.println("index:===========end=========="); ??? } ????? ??? /** ???? * @param args ???? */ ??? public static void main(String[] args) { ??????? try { ??????????? index(); ??????? } catch (Exception e) { ??????????? ??????????? e.printStackTrace(); ??????? }?????? ??? } }
? 第四步:檢索程序實現 package search; import java.util.Hashtable; import chapter2.index.InfoItem; /** * @author gf * */ public class SearchWordIndex { ??? private static Hashtable keyWordIdx; //關鍵詞哈希表 ??? //存放原始文件內容,每一數組代表一個文件 ??? private static String [] FileList = {"北京師范大學", ??????? "北師大附屬試驗小學","北師大第二附屬中學"}; ??? ?? ??? //根據給定的內容,按照漢字建立Hash索引 ??? public static void index() throws Exception{ ??????? InfoItem item1,item2; ??????? System.out.println("index:===========begin=========="); ??????? keyWordIdx = new Hashtable(); //創建關鍵詞Hash表 ??????? ??????? //關鍵詞初始長度 ??????? System.out.println("index:Hashtable initial Size:"+keyWordIdx.size()); ??????? ??????? for(int i=0;i<3;i++){ ??????????? int len = FileList[i].length(); ??????????? for(int j =0;j<len;j++){ ??????????????? item1 = new InfoItem(i,i); //生產文件存放位置的附屬信息 ??????????????? String key = FileList[i].substring(j,j+1); ??????????????? System.out.print(key); ??????????????? if(!keyWordIdx.containsKey(key)){ ??????????????????? keyWordIdx.put(key, item1); ??????????????? }else{ ??????????????????? //提取原始的存儲項 ??????????????????? item2 = (InfoItem) keyWordIdx.get(key); ??????????????????? item1.setNext(item2); //新文件節點添加到鏈表頭 ??????????????????? keyWordIdx.put(key, item1);//添加關鍵字索引到Hash表 ??????????????? } ??????????? } ??????? } ??????? System.out.println("\nindex:Hashtable finish Size:"+keyWordIdx.size()); ??????? System.out.println("index:===========end=========="); ??? } ??? ??? //根據輸入的內容進行Hash檢索 ??? public static void search(String keyword) throws Exception{ ??????? InfoItem item; ??????? System.out.println("Search:=======begin======="); ??????? if(null == keyWordIdx){ ??????????? return; ??????? } ??????? //根據關鍵詞獲取Hash表中的索引項列表 ??????? item = (InfoItem)keyWordIdx.get(keyword); ??????? //循環顯示索引項內容 ??????? while(null != item){ ??????????? //顯示編號 ??????????? System.out.println("Search:File number :"+item.getFileID()); ??????????? //顯示偏移 ??????????? System.out.println("Search:File offset :"+item.getOffset()); ??????????? //顯示內容 ??????????? System.out.println("Search:File Content :"+FileList[item.getOffset()]); ??????????? ??????????? //獲取下一個索引項 ??????????? item = item.getNext(); ??????? } ??????? System.out.println("Search:=======end======="); ??? } ??? ??? /** ???? * @param args ???? */ ??? public static void main(String[] args) { ??????? try { ??????????? index(); ??????????? search("試"); ??????? } catch (Exception e) { ??????????? ??????????? e.printStackTrace(); ??????? }?????? ??? } } |