接着上一節的內容,我們已經構建了一個爬取百度百科並持久化的爬蟲。現在客戶使用,我們需要構建一個搜索工具。
1.信息檢索
這個項目的下一個階段是實現一個搜索工具。我們需要的部分包括:
- 一個界面,其中用戶可以提供檢索詞並查看結果。
- 一種查找機制,它接收每個檢索詞並返回包含它的頁面。
- 用於組合來自多個檢索詞的搜索結果的機制。
- 對搜索結果打分和排序的算法。
2.布爾搜索
大多數搜索引擎可以執行“布爾搜索”,這意味着你可以使用布爾邏輯來組合來自多個檢索詞的結果。例如:
- 搜索“java + 編程”(加號可省略)可能只返回包含兩個檢索詞:“java”和“編程”的頁面。
- “java OR 編程”可能會返回包含任一檢索詞但不一定同時出現的頁面。
- “java -印度尼西亞”可能返回包含“java”,不包含“印度尼西亞”的頁面。
包含檢索詞和運算符的表達式稱爲“查詢”。
當應用給搜索結果時,布爾操作符+
,OR
和-
對應於集合操作 交,並和差。例如,假設
s1
是包含“java”的頁面集,s2
是包含“編程”的頁面集,以及s3
是包含“印度尼西亞”的頁面集。
在這種情況下:
s1
和s2
的交集是含有“java”和“編程”的頁面集。s1
和s2
的並集是含有“java”或“編程”的頁面集。s1
與s2
的差集是含有“java”而不含有“印度尼西亞”的頁面集。
布爾搜索實現:
/**
* @Author Ragty
* @Description 計算兩個搜索結果的並集(更廣了)
* @Date 23:13 2019/5/30
**/
public WikiSearch or(WikiSearch that) {
Map<String,Integer> union = new HashMap<String, Integer>(map);
for(String term: that.map.keySet()) {
union.put(term,totalRelevence(this.getRelevance(term),that.getRelevance(term)));
}
return new WikiSearch(union);
}
/**
* @Author Ragty
* @Description 計算搜索項的交集
* @Date 23:22 2019/5/30
**/
public WikiSearch and(WikiSearch that) {
Map<String,Integer> intersection = new HashMap<String, Integer>();
for(String term: that.map.keySet()) {
if(map.containsKey(term)) {
intersection.put(term,totalRelevence(this.getRelevance(term),that.getRelevance(term)));
}
}
return new WikiSearch(intersection);
}
/**
* @Author Ragty
* @Description 計算搜索項的差集
* @Date 23:35 2019/5/30
**/
public WikiSearch minus(WikiSearch that) {
Map<String,Integer> difference = new HashMap<String, Integer>(map);
for (String term: that.map.keySet()) {
if (difference.containsKey(term)) {
difference.remove(term);
}
}
return new WikiSearch(difference);
}
3.打分排序
現在需要根據檢索詞獲取用戶最需要的數據,WikiSearch
對象包含 URL 到它們的相關性分數的映射。在信息檢索的上下文中,“相關性分數”用於表示頁面多麼滿足從查詢推斷出的用戶需求。相關性分數的構建有很多種方法,但大部分都基於“檢索詞頻率”,它是搜索詞在頁面上的顯示次數。一種常見的相關性分數稱爲 TF-IDF,代表“檢索詞頻率 - 逆向文檔頻率”。
下面是WikiSearch
的初始構造:
public class WikiSearch {
// map from URLs that contain the term(s) to relevance score
private Map<String, Integer> map;
public WikiSearch(Map<String, Integer> map) {
this.map = map;
}
public Integer getRelevance(String url) {
Integer relevance = map.get(url);
return relevance==null ? 0: relevance;
}
}
WikiSearch
對象包含 URL 到它們的相關性分數的映射。在信息檢索的上下文中,“相關性分數”用於表示頁面多麼滿足從查詢推斷出的用戶需求。相關性分數的構建有很多種方法,但大部分都基於“檢索詞頻率”,它是搜索詞在頁面上的顯示次數。一種常見的相關性分數稱爲 TF-IDF,代表“檢索詞頻率 - 逆向文檔頻率”。
我們將從一些更簡單的 TF 開始:
- 如果查詢包含單個檢索詞,頁面的相關性就是其詞頻;也就是說該詞在頁面上出現的次數。
- 對於具有多個檢索詞的查詢,頁面的相關性是檢索詞頻率的總和;也就是說,任何檢索詞出現的總次數。
搜索結果排序實現:
public List<Entry<String,Integer>> sort(){
List<Entry<String,Integer>> entries = new LinkedList<Entry<String, Integer>>(map.entrySet());
Comparator<Entry<String,Integer>> comparator = new Comparator<Entry<String, Integer>>() {
@Override
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
return o2.getValue().compareTo(o1.getValue());
}
};
Collections.sort(entries,comparator);
return entries;
}
有兩個sort
版本:
- 單參數版本接受列表並使用它的
compareTo
方法對元素進行排序,因此元素必須是Comparable
。 - 雙參數版本接受任何對象類型的列表和一個
Comparator
,它是一個提供compare
方法的對象,用於比較元素。
4.檢索測試?
下面提供檢索代碼:
public static void main(String[] args) throws IOException {
Jedis jedis = JedisMaker.make();
JedisIndex index = new JedisIndex(jedis);
// search for the first term
String term1 = "java";
System.out.println("Query: " + term1);
WikiSearch search1 = search(term1, index);
search1.print();
// search for the second term
String term2 = "programming";
System.out.println("Query: " + term2);
WikiSearch search2 = search(term2, index);
search2.print();
// compute the intersection of the searches
System.out.println("Query: " + term1 + " AND " + term2);
WikiSearch intersection = search1.and(search2);
intersection.print();
}
檢索結果爲:
Query: java?
https://baike.baidu.com/item/Java/85979 38
https://baike.baidu.com/item/%E6%AC%A7%E6%9C%8B%E6%B5%8F%E8%A7%88%E5%99%A8 12
https://baike.baidu.com/item/%E7%94%B2%E9%AA%A8%E6%96%87/471435 10
https://baike.baidu.com/item/PHP/9337 6
https://baike.baidu.com/item/Brendan%20Eich 6
https://baike.baidu.com/item/Rhino 6
https://baike.baidu.com/item/LiveScript 6
https://baike.baidu.com/item/Sun/69463 4
Query: programming?
https://baike.baidu.com/item/C%E8%AF%AD%E8%A8%80 6
https://baike.baidu.com/item/%E8%84%9A%E6%9C%AC%E8%AF%AD%E8%A8%80 1
Query: java AND programming?
https://baike.baidu.com/item/C%E8%AF%AD%E8%A8%80 7
https://baike.baidu.com/item/%E8%84%9A%E6%9C%AC%E8%AF%AD%E8%A8%80 3
5.搜索優化
現存的問題:
若同一主題的文章,A1萬字,它的關鍵詞出現的次數肯定會高,B100字,出現的次數肯定會少。對於具有多個檢索詞的查詢,每個頁面的總體相關性目前是每個檢索詞的相關性的總和。但實際上採用我們上述所用的方法不能客觀的去選出內容最符合的文章。
我們需要的解決方案:
去掉網絡中止詞後,顯示一個詞在文本中所出現的頻率,頻率越高,則說明該搜索詞與文章的相關性越大。
後期可用
TF-IDF
算法優化