ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java项目中接入jieba分词:从选型到Spring Boot实战

Java项目中接入jieba分词:从选型到Spring Boot实战 简介面向Java开发者的jieba分词Java版项目解决在Java环境中复用中文分词能力的工程化需求适合需要处理搜索分词、文本挖掘或自然语言预处理的初中级开发者。压缩包共30个文件含10个可读Java源码、11个编译后class文件、4个词典配置文件及Eclipse工程相关xml/prefs/project文件整体仅4.17MB解压后即可通过Eclipse导入。项目封装自开源jieba分词算法提供精确模式、全模式和搜索引擎模式三种分词策略并支持基于TF-IDF的关键词提取与词性标注扩展。已有2207人学习工程内run包下的test程序作为演示入口可运行观察分词结果同时可通过更新dict.txt词典、调整参数或组合其他Java库适配专有名词识别、大规模文本批量处理等具体场景。 按理说一个做Java的人跟“jieba”这个名字的缘分往往是这么开始的先在Python里用了一下jieba发现真香分词又快又准于是想在自己Java后端项目里也这么整。结果一搜发现jieba本身是Python写的Java要用就得找移植版或者第三方封装版本还不少。更麻烦的是搜出来的教程大多是从Python角度写的直接告诉你pip install jieba找不到几个能讲清楚Java里怎么导入、怎么封装、怎么接进Spring Boot的。这篇文章就是解决这个问题的。我会从Java版的选型讲起把常用的几个库捋一遍然后直接上代码从最基础的依赖引入到跑通分词再接到Spring Boot项目里最后说说我在实际项目中踩过的坑。面向的读者是那种“会Spring Boot基础、但没怎么碰过中文分词”的Java开发者以及从Python临时转过来的同学。1. 先搞清楚你要的“Java版jieba”其实是哪个项目如果想直接用Maven拉一个com.huaban:jieba-analysis然后以为这就是Python版jieba的官方Java实现那方向就偏了。这两个东西的关系严格来说是“同一套分词思路的两种实现”不是同一个组织维护的。1.1 原版Python与Java移植版的来龙去脉Python版的jieba是Sun Junyi结巴开源的中文分词库底层是前缀词典加动态规划再配合隐马尔可夫模型处理未登录词GitHub上接近三万的star已经说明了它在Python中文处理领域的地位。Java这边的情况比较零散主要流派有三个com.huaban:jieba-analysis这个是最早被大家用起来的Java版由蘑菇街的工程师维护。它复刻了Python版的核心思路前缀词典 DAG有向无环图 HMM识别新词API风格也尽量贴近Python版。com.github.gottingen:analyzer-jieba早期在GitHub上传的比较广的一个版本但维护热度一般代码风格相对旧一些。各类自研改写版在Gitee和GitHub上散落着不少个人改写的版本有的加进了lucene插件有的优化了内存占用。如果一个Java项目要接入中文分词我的建议是优先看维护活跃度而不是光看下载量。在这个领域com.huaban:jieba-analysis虽然也有几年没大更新了但它结构简单、无重依赖、文档齐全作为业务项目里的分词组件是完全够用的。1.2 为什么不直接用HanLP或者Lucene自带的SmartChinese聊Java分词很多人会提HanLP。HanLP功能确实强大支持分词、词性标注、命名实体识别、依存句法分析一大堆模型训练的复杂度也高得多。但问题在于占用的依赖体积和内存比jieba-analysis大一个量级如果业务场景只是“把用户搜索词切分成几个关键词”上HanLP属于杀鸡用牛刀。Lucene自带的中文分词器SmartChinesesmartcn也是一个方案但它跟Lucene的版本耦合太紧单独拿出来用在普通业务代码里封装成本偏高分词效果在细分场景下也明显不如jieba清晰。所以做选型的时候核心判断标准是这个库是来解决一个具体问题的还是来替代你的业务中间件的。只做词频统计、搜索词切分、舆情关键词提取这些轻量任务jieba-analysis反而更顺手。2. Maven依赖引入这一步就能卡掉不少人理论上引入jieba-analysis只需要一个dependency。但因为官方文档年久失修实际使用时的坑都在细节里。2.1 正确的坐标与版本选择Maven坐标长这样dependency groupIdcom.huaban/groupId artifactIdjieba-analysis/artifactId version1.0.2/version /dependency这个1.0.2版本是最新也是最后一个release版本。注意不是0.0.1-SNAPSHOT这种测试版别看到版本号老就觉得不稳。从JDK版本上来讲这个库用到了Java 7的语法所以在JDK 8到JDK 17的项目里都能正常跑。唯一要注意的是如果项目用的是JDK 9以上模块化环境需要添加--add-opens的情况很少我实测下来基本不需要。2.2 依赖冲突与传递依赖的坑jieba-analysis本身几乎不依赖第三方包这点很省心。但是如果你项目里同时用了com.github.jsqlparser这类包偶尔会出现asm相关的jar包版本冲突因为旧版本jieba-analysis内部会用一点ASM做字节码优化这种冲突报错通常长这样java.lang.NoClassDefFoundError: org/objectweb/asm/ClassVisitor遇到这个先在依赖树里确认是哪一层引入的老版ASMmvn dependency:tree -Dincludesorg.ow2.asm:asm然后统一排除掉保留ASM 5以上版本即可。多模块项目尤其建议在父POM中做好全局的ASM版本管理。3. 常用API实测分词器、词性标注、关键词提取依赖配好之后就可以直接写代码了。JiebaSegmenter是核心类它的用法非常简单但要想让它在业务里真正好用还得花点心思封装。3.1 三种切分模式的差异和选择JiebaSegmenter支持三种切分模式分别对应Python版里的精确模式、全模式和搜索引擎模式JiebaSegmenter segmenter new JiebaSegmenter(); // 1. 精确模式默认适合文本分析 ListSegToken tokens segmenter.process(我来到北京清华大学, JiebaSegmenter.SegMode.INDEX); // 2. 全模式把所有可能成词的词语都扫描出来速度快但会有冗余 ListSegToken tokens segmenter.process(我来到北京清华大学, JiebaSegmenter.SegMode.SEARCH); // 3. 搜索引擎模式在精确模式基础上对长词再次切分适合搜索场景 ListSegToken tokens segmenter.process(我来到北京清华大学, JiebaSegmenter.SegMode.SEARCH);稍微分析一下这几个模式的差异INDEX模式下“清华大学”会被切分成一个整体。SEARCH模式下它会进一步切出“清华”“大学”“北京”等更细粒度的词方便搜索引擎召回更多结果。实际在搜索引擎里做分词SEARCH模式更常用因为搜索场景的用户输入往往比较短、信息密度高切成细粒度词可以提高召回率。而做文本聚类、关键词提取用INDEX模式更稳。还有一个process重载方法可以直接处理String文本也可以传ListString批量处理底层对批量场景有优化。3.2 词性标注和关键词提取其实要靠自己算Python版jieba自带jieba.posseg做词性标注但Java版的jieba-analysis没有内置这个词性标注功能。这并不是不好只是说明这个Java版更专注于“切词”本身。如果需要词性常见做法是分词后自己去匹配一个词性词典。关键词提取也是类似Java版没有现成的TF-IDF接口但我们可以用一句流式代码配合WordWeight自己实现。这里给出一个实际能跑的简单实现public MapString, Integer extractKeywords(String text, int topN) { JiebaSegmenter segmenter new JiebaSegmenter(); ListSegToken tokens segmenter.process(text, JiebaSegmenter.SegMode.INDEX); MapString, Integer freq new HashMap(); for (SegToken token : tokens) { String word token.word.trim(); if (word.length() 2) { continue; // 太短的词不参与关键词 } freq.put(word, freq.getOrDefault(word, 0) 1); } return freq.entrySet().stream() .sorted(Map.Entry.String, IntegercomparingByValue().reversed()) .limit(topN) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) - e1, LinkedHashMap::new)); }这里在统计之前过滤掉长度为1的词很重要否则“的”“了”“是”这类单字会频繁霸榜。更进一步可以准备一个小型的停用词表把“我们”“可以”“因为”这类无实际意义的词过滤掉。4. 把jieba装进Spring Boot从Demo到生产线很多教程止步于“跑通Process”但真实项目里你不可能在每次要分词的时候都new JiebaSegmenter()那样性能和内存都会很浪费。合理的做法是交给Spring管理成一个单例Bean。4.1 封装成Spring Bean的正确姿势Component public class JiebaTokenizer { private final JiebaSegmenter segmenter new JiebaSegmenter(); public ListString cut(String text) { return segmenter.process(text, JiebaSegmenter.SegMode.SEARCH) .stream() .map(token - token.word) .collect(Collectors.toList()); } public ListString cutForAnalyze(String text) { return segmenter.process(text, JiebaSegmenter.SegMode.INDEX) .stream() .map(token - token.word) .collect(Collectors.toList()); } }JiebaSegmenter内部使用的是不可变的前缀词典初始化时加载词典之后的process调用是线程安全的。所以单例Beans可以安全地在多线程环境下使用。设计两个接口方法的好处是搜索接口用SEARCH模式业务分析模块用INDEX模式互不干扰。如果后续要把分词器替换成其他实现比如HanLP只需要改这个类对上层调用方透明。4.2 针对业务场景给它加一层缓存分词本身性能不错中文一千字的文本实测大概在10-30毫秒但对那种热点词频繁命中的场景没必要每次都重新切一遍。一个简单的ConcurrentHashMap做缓存就够了Component public class JiebaTokenizer { private final JiebaSegmenter segmenter new JiebaSegmenter(); private final CacheString, ListString cache Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); public ListString cutWithCache(String text) { return cache.get(text, key - cut(key)); } }热点搜索词往往重复度很高加一层缓存后整体吞吐能提升好几倍。这里用的是Caffeine在Spring Boot项目里通常会自带或很容易引入不需要额外的复杂配置。5. 生产环境里的常见坑从词典到并发这些坑是我在实际项目中一个一个踩出来的也是排查时间最长、最没有文档可查的部分。写在这帮大家直接避过去。5.1 自定义词典的编码问题最容易踩jieba-analysis支持自定义词典在代码中指定字典文件路径JiebaSegmenter segmenter new JiebaSegmenter(); // 加载自定义词典字典文件为UTF-8编码每行一个词 segmenter.loadUserDict(path/to/user_dict.txt);这个功能很好用可以把“光子嫩肤”“区块链溯源”“李佳琦”这样的专有词加进去。但词典文件必须是UTF-8编码如果你的文件是GBK编码分词时不仅不生效还可能出现乱码错词。一个隐蔽的问题是在Windows下用记事本另存为UTF-8时会带入BOM头\uFEFF导致词典加载后第一个词拼不进去。解决办法是用Notepad或VS Code另存为“UTF-8无BOM”格式或者加载后手动做一次BOM清理private String cleanBom(String word) { if (word.startsWith(\uFEFF)) { return word.substring(1); } return word; }5.2 并发环境下的性能表现JiebaSegmenter的核心数据结构是前缀词典在process过程中不修改词库所以多线程并发读取是安全的。实测在一个4核8线程的实例上100个并发线程同时请求分词每分钟可以处理大约3000段短文本每段50字以内性能相当可观。但有一个比较吃内存的点JiebaSegmenter在初始化时会加载默认词典默认词典包含大约35万个词条内存占用大约在80MB左右。在Spring Boot内置的容器里这个内存开销不算小所以要注意JVM配置尤其是用容器部署时别把堆内存压得太死。5.3 数字、英文、日期混合文本的边界问题中文分词器面对“iPhone15Pro”这种中英混排文本时简单调用process后结果可能会变成“iphone”“15”“pro”三个词这对于商品搜索来说也许不够理想。如果业务需要保留原始词条可以在分词前先做一次正则判断把中英数字混排的整词摘出来剩下的再给jieba处理public ListString smartCut(String text) { ListString result new ArrayList(); Matcher matcher Pattern.compile([a-zA-Z0-9]|[\\u4e00-\\u9fa5]).matcher(text); while (matcher.find()) { String part matcher.group(); if (part.matches([a-zA-Z0-9])) { result.add(part); } else { result.addAll(cutForAnalyze(part)); } } return result; }这样可以规避掉jieba在纯英文数字串上的分割让其集中精力处理中文部分效果明显更好。6. 从分词到业务落地场景和后续扩展分词本身不是目的目的永远是下游业务的准确度。这里列两个我实际做过的落地场景给大家一个参考方向。6.1 启用搜索建议电商和内容类产品的搜索框一般都要做“搜索建议”——用户输入“防晒”下拉框要提示“防晒霜”“防晒喷雾”“防晒衣”。这个功能在数据量不大的时候就是先对商品标题或文章标题做jieba分词再建立前缀索引。用户每输入一个字就去匹配前缀把热门的词返回上来。关键词分析加上统计后续整个链路大概是文章标题或商品名 - jieba分词 - 词频统计 - 清洗噪声词 - 建立倒排索引或前缀树 - 搜索框建议。jieba在这条链路里承担的是第一步也是最关键的一步分词质量直接决定了后续所有环节的准确度。6.2 舆情热点词分析给一个企业客户做过一个舆情系统每天爬取数万条新闻和社交媒体内容然后用jieba分词结合词频统计输出每个时段的热点话题词。刚上线时发现“苹果”这个词的词频很高点进去一看大量是“苹果手机”的新闻还有一些是“苹果价格”的生鲜新闻这两类完全不是一个行业。后来解决方案是先分词再人工维护一组“行业限定词”比如在生鲜场景下“苹果”后面经常接“价格”“批发”在数码场景下接“手机”“发布会”用同现频率做二次过滤之后准确率从70%提到了90%以上。这个案例的启示是分词工具不要追求一步到位核心是分词结果能不能和你的业务词典、业务规则衔接起来。从我的实际经验看如果你只是需要一个稳定、轻量、上手快的中文分词组件Java版jieba仍然是性价比最高的选择之一。它的代码量不大源码本身就是一个很好的学习样本看完能理解前缀词典、DAG、动态规划这些底层概念在真实项目里是怎么落地的。如果想继续深入还可以去读读Python版里HMM识别新词的实现思路然后想想怎么在Java里复刻。这个坑值得跳。本文还有配套的精品资源点击获取
返回列表