一、需求
给出一篇新闻文档,统计出现频率最高的有哪些词语。
二、思路
关于文本关键词提取的算法有很多,开源工具也不止一种。这里只介绍如何从Lucene索引中提取词项频率的TopN。索引过程的本质是一个词条化的生存倒排索引的过程,词条化会从文本中去除标点符号、停用词等,最后生成词项。在代码中实现的思路是使用IndexReader的getTermVector获取文档的某一个字段的Terms,从terms中获取tf(term frequency)。拿到词项的tf以后放到map中降序排序,取出Top-N。
三、代码实现
工程目录如下:
关于Lucene 6.0中如何使用IK分词,请参考http://blog.csdn.net/napoay/article/details/51911875。工程里重要的只有2个类,IndexDocs.java和GetTopTerms.java。
3.1索引新闻
在百度新闻上随机找了一篇新闻:李开复:无人驾驶进入黄金时代 AI有巨大投资机会,新闻内容为李开复关于人工智能的主题演讲。把新闻的文本内容放在testfile/news.txt文件中。IndexDocs中的内容:
package lucene.test
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException
import java.nio.file.Paths
import org.apache.lucene.analysis.Analyzer
import org.apache.lucene.document.Document
import org.apache.lucene.document.Field
import org.apache.lucene.document.FieldType
import org.apache.lucene.index.IndexOptions
import org.apache.lucene.index.IndexWriter
import org.apache.lucene.index.IndexWriterConfig
import org.apache.lucene.index.IndexWriterConfig.OpenMode
import org.apache.lucene.store.Directory
import org.apache.lucene.store.FSDirectory
import lucene.ik.IKAnalyzer6x
public class IndexDocs {
public static void main(String[] args) throws IOException {
File newsfile = new File("testfile/news.txt")
String text1 = textToString(newsfile)
// Analyzer smcAnalyzer = new SmartChineseAnalyzer(true)
Analyzer smcAnalyzer = new IKAnalyzer6x(true)
IndexWriterConfig indexWriterCOnfig= new IndexWriterConfig(smcAnalyzer)
indexWriterConfig.setOpenMode(OpenMode.CREATE)
// 索引的存储路径
Directory directory = null
// 索引的增删改由indexWriter创建
IndexWriter indexWriter = null
directory = FSDirectory.open(Paths.get("indexdir"))
indexWriter = new IndexWriter(directory, indexWriterConfig)
// 新建FieldType,用于指定字段索引时的信息
FieldType type = new FieldType()
// 索引时保存文档、词项频率、位置信息、偏移信息
type.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS)
type.setStored(true)
type.setStoreTermVectors(true)
type.setTokenized(true)
Document doc1 = new Document()
Field field1 = new Field("content", text1, type)
doc1.add(field1)
indexWriter.addDocument(doc1)
indexWriter.close()
directory.close()
}
public static String textToString(File file) {
StringBuilder result = new StringBuilder()
try {
BufferedReader br = new BufferedReader(new FileReader(file))
String str = null
while ((str = br.readLine()) != null) {// 使用readLine方法,一次读一行
result.append(System.lineSeparator() + str)
}
br.close()
} catch (Exception e) {
e.printStackTrace()
}
return result.toString()
}
}
3.2获取热词
package lucene.test
import java.io.IOException
import java.nio.file.Paths
import java.util.ArrayList
import java.util.Collections
import java.util.Comparator
import java.util.HashMap
import java.util.List
import java.util.Map
import java.util.Map.Entry
import org.apache.lucene.index.DirectoryReader
import org.apache.lucene.index.IndexReader
import org.apache.lucene.index.Terms
import org.apache.lucene.index.TermsEnum
import org.apache.lucene.store.Directory
import org.apache.lucene.store.FSDirectory
import org.apache.lucene.util.BytesRef
public class GetTopTerms {
public static void main(String[] args) throws IOException {
//
Directory directory = FSDirectory.open(Paths.get("indexdir"))
IndexReader reader = DirectoryReader.open(directory)
// 因为只索引了一个文档,所以DocID为0,通过getTermVector获取content字段的词项
Terms terms = reader.getTermVector(0, "content")
// 遍历词项
TermsEnum termsEnum = terms.iterator()
BytesRef thisTerm = null
Map map = new HashMap()
while ((thisTerm = termsEnum.next()) != null) {
// 词项
String termText = thisTerm.utf8ToString()
// 通过totalTermFreq()方法获取词项频率
map.put(termText, (int) termsEnum.totalTermFreq())
}
// 按value排序
List
四、结果分析
4.1SmartChineseAnalyzer提取结果
第一次的结果是使用Lucene自带的SmartChineseAnalyzer分词得出来的top-10的结果.很显然这个结果并不是我们期待的。
4.2IKAnalyzer提取结果
换成IK分词,结果依然糟糕。问题出在停用词太多,的、是、我、这、了这种词出现频率非常高,但是并没有意义。
4.3Ik+扩展停用词表提取结果
下载哈工大中文停用词词表,加到src/stopword.dic中,重新索引文档,再次运行GetTopTerms.java。结果如下:
新闻的内容是李开复关于人工智能、无人驾驶、AI的演讲,这次的提取结果比较靠谱。
五、参考文献
1、In lucene 4, IndexReader.getTermVector(docID, fieldName) returns null for every doc
2、Lucene索引过程分析
六、源码
欢迎批评指正。
需要源码的可以加入Lucene、ES、ELK开发交流群(370734940)下载。