<!-- # 提取小说的主角名-Java --> <!-- pick-up-major-characters-from-novel-by-java --> Python 中有个 *jieba* 组件可以进行分词,项目使用的 Java,找到一个[Java版的jieba][1],但是功能比较简单,只有分词,不支持人名识别。 另外还找到一个[HanLP汉语言处理包][3],这个功能比较多,支持人名识别、关键词提取、自动摘要等功能,用法可以参考[官方的1.x版文档][4]。 这里基于上述两个工具测试了一下提取小说中的主角名(人名出现次数最多的两个算主角)。 ### 1. 分别添加2个依赖 ```xml <!-- Jieba --> <dependency> <groupId>com.huaban</groupId> <artifactId>jieba-analysis</artifactId> <version>1.0.2</version> </dependency> <!-- Hanlp --> <dependency> <groupId>com.hankcs</groupId> <artifactId>hanlp</artifactId> <version>portable-1.8.3</version> </dependency> ``` ### 2. 测试代码 这里省略了小说章节的内容,另外 *jieba* 没有人名识别,参考[这篇博客][5]用正则表达式验证是否是人名。 ```java import com.hankcs.hanlp.HanLP; import com.hankcs.hanlp.corpus.tag.Nature; import com.hankcs.hanlp.seg.Segment; import com.hankcs.hanlp.seg.common.Term; import com.huaban.analysis.jieba.JiebaSegmenter; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import reactor.util.function.Tuple2; import reactor.util.function.Tuples; import java.util.Comparator; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; public class NameTest { private final String chapter = "{这里省略了章节内容}"; private final Pattern namePattern = Pattern.compile( "(王|李|张|刘|陈|杨|黄|赵|吴|周|徐|孙|马|朱|胡|郭|何|高|林|罗|郑|梁|谢|宋|唐|许|韩|冯|邓|曹|彭|曾" + "|肖|田|董|袁|潘|于|蒋|蔡|余|杜|叶|程|苏|魏|吕|丁|任|沈|姚|卢|姜|崔|钟|谭|陆|汪|范|金|石|廖|贾|夏|韦|傅" + "|方|白|邹|孟|熊|秦|邱|江|尹|薛|闫|段|雷|侯|龙|史|黎|贺|顾|毛|郝|龚|邵|万|钱|武|戴|孔|汤|庞|樊|兰|殷" + "|施|陶|洪|翟|安|颜|倪|严|牛|温|芦|季|俞|章|鲁|葛|伍|申|尤|毕|聂|柴|焦|向|柳|邢|岳|齐|沿|梅|莫|庄|辛|管" + "|祝|左|涂|谷|祁|时|舒|耿|牟|卜|路|詹|关|苗|凌|费|纪|靳|盛|童|欧|甄|项|曲|成|游|阳|裴|席|卫|查|屈|鲍|位" + "|覃|霍|翁|隋|植|甘|景|薄|单|包|司|柏|宁|柯|阮|桂|闵|欧阳|解|强|丛|华|车|冉|房|边|辜|吉|饶|刁|瞿|戚|丘" + "|古|米|池|滕|晋|苑|邬|臧|畅|宫|来|嵺|苟|全|褚|廉|简|娄|盖|符|奚|木|穆|党|燕|郎|邸|冀|谈|姬|屠|连|郜|晏" + "|栾|郁|商|蒙|计|喻|揭|窦|迟|宇|敖|糜|鄢|冷|卓|花|艾|蓝|都|巩|稽|井|练|仲|乐|虞|卞|封|竺|冼|原|官|衣|楚" + "|佟|栗|匡|宗|应|台|巫|鞠|僧|桑|荆|谌|银|扬|明|沙|伏|岑|习|胥|保|和|蔺|水|云|昌|凤|酆|常|皮|康|元|平" + "|萧|湛|禹|无|贝|茅|麻|危|骆|支|咎|经|裘|缪|干|宣|贲|杭|诸|钮|嵇|滑|荣|荀|羊|於|惠|家|芮|羿|储|汲|邴|松" + "|富|乌|巴|弓|牧|隗|山|宓|蓬|郗|班|仰|秋|伊|仇|暴|钭|厉|戎|祖|束|幸|韶|蓟|印|宿|怀|蒲|鄂|索|咸|籍|赖|乔" + "|阴|能|苍|双|闻|莘|贡|逢|扶|堵|宰|郦|雍|却|璩|濮|寿|通|扈|郏|浦|尚|农|别|阎|充|慕|茹|宦|鱼|容|易|慎|戈" + "|庚|终|暨|居|衡|步|满|弘|国|文|寇|广|禄|阙|东|殴|殳|沃|利|蔚|越|夔|隆|师|厍|晃|勾|融|訾|阚|那|空|毋|乜" + "|养|须|丰|巢|蒯|相|后|红|权逯|盖益|桓|公|万俟|司马|上官|夏侯|诸葛|闻人|东方|赫连|皇甫|尉迟|公羊|澹台" + "|公冶|宗政|濮阳|淳于|单于|太叔|申屠|公孙|仲孙|轩辕|令狐|钟离|宇文|长孙|慕容|鲜于|闾丘|司徒|司空|亓官" + "|司寇|仉|督|子车|颛孙|端木|巫马|公西|漆雕|乐正|壤驷|公良|拓跋|夹谷|宰父|谷粱|法|汝|钦|段干|百里|东郭" + "|南门|呼延|归海|羊舌|微生|帅|缑|亢|况|郈|琴|梁丘|左丘|东门|西门|佘|佴|伯|赏|南宫|墨|哈|谯|笪|年|爱|仝|代)[\u4E00-\u9FA5]{1,6}"); @Test public void segmentByJieba() { JiebaSegmenter segmenter = new JiebaSegmenter(); List<String> words = segmenter.sentenceProcess(chapter); // System.out.println(words); List<Tuple2<String, Long>> names = words.stream() .filter(StringUtils::isNotEmpty) .filter(s -> s.length() > 1) .filter(this::isName) .collect(Collectors.groupingBy(v -> v, Collectors.counting())) .entrySet() .stream() .filter(s -> s.getValue() > 1) .map(s -> Tuples.of(s.getKey(), s.getValue())) .sorted(Comparator.comparingLong(Tuple2<String, Long>::getT2).reversed()) .limit(2) .collect(Collectors.toList()); System.out.print("使用 Jieba 分词:"); System.out.println(names); } private boolean isName(String str) { return namePattern.matcher(str).find(); } @Test public void segmentByHanLP() { Segment segment = HanLP.newSegment().enableNameRecognize(true); List<Term> terms = segment.seg(chapter); // System.out.println(terms); List<Tuple2<String, Long>> names = terms.stream() .filter(m -> m.nature == Nature.nr) .collect(Collectors.groupingBy(v -> v.word, Collectors.counting())) .entrySet() .stream() .filter(s -> s.getValue() > 1) .map(s -> Tuples.of(s.getKey(), s.getValue())) .sorted(Comparator.comparingLong(Tuple2<String, Long>::getT2).reversed()) .limit(2) .collect(Collectors.toList()); System.out.print("使用 HanLP 分词:"); System.out.println(names); } } ``` ### 3. 测试结果 这是使用一个章节的测试结果: ```javascript 使用 HanLP 分词:[[怀素,9], [顾清,9]] 使用 Jieba 分词:[[怀素,16], [顾清,9]] ``` 可以看到,具体的分词结果上还是有些区别的,第一个人名的统计结果差的有点多。 不过感觉增加统计的章节内容的话,准确率应该会高很多。 [1]:https://mvnrepository.com/artifact/com.huaban/jieba-analysis (结巴分词工具(jieba For Java)) [2]:https://mvnrepository.com/artifact/com.hankcs/hanlp (汉语言处理包HanLP) [3]:https://github.com/hankcs/HanLP (hankcs - HanLP) [4]:https://github.com/hankcs/HanLP/tree/1.x (HanLP: Han Language Processing) [5]:https://blog.csdn.net/lijunliang2017/article/details/121556109 (java从文本中提取姓名、身份证、电话、地址) Loading... 版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://www.liujiajia.me/2022/4/15/pick-up-major-characters-from-novel-by-java 提交