Spark+Jieba实现中文分词

本案例使用jieba分词,jieba 是目前最好的 Python 中文分词组件,使用Spark同样也能实现中文分词。实现步骤如下:

1.导入依赖

<dependency>
            <groupId>com.huaban</groupId>
            <artifactId>jieba-analysis</artifactId>
            <version>1.0.2</version>
        </dependency>

2.数据样本截取

8920397333	王铮亮 时间都去哪了 《私人订制》插曲
8920408333	Locked Out Of Heaven 音乐高清视频MV
8920422333	影视-心上人啊快给我力量KTV(电影《神圣的使命》插曲
8920491333	068_奥特曼
8920492333	影视-幸福不会从天降KTV(电影《我们村里的年轻人》插
8920527333	邓紫棋 GEM 2013 X.X.X. LIVE 演唱会 【全场高清】
8920529333	067_外婆的澎湖湾
8920588333	卓依婷-纸飞机
8920622333	073_小红帽儿歌
8920623333	072_字母歌
8920624333	影视-星星知我心KTV(台湾电视剧《星星知我心》主题曲
8920650333	2014蔡依林新年歌曲《新年喜洋洋》
8920702333	《Love Me》Justin Bieber感谢歌迷最新单曲
8920717333	075_只要妈妈露笑脸
8920731333	外婆的澎湖湾(电音dj舞曲)
8920745333	新西兰小伙,罗艺恒   加油  不插电现场版
8920787333	少女部落格2014迎新年首播MV《恭喜好运来》
8920791333	天路MV-韩红
8920845333	初音未来PV【世界第一公主殿下】
8920849333	曼莉(dj电音舞曲)
8920888333	《我是歌手》第四场无歌单惊呆众歌手!
8920909333	【MV首播】野人-EveN MV(完整HD版)
8920922333	影视新势力 美女偶像 景甜 风---  电影 战国 主题曲
8920944333	【刘德华高清MV合集】真永远 高清
8920956333	郑源-难道爱一个人有错吗[高清MV街](流畅)
8920982333	500.甄妮 鲁冰花 演唱会 热门MV MTV 音乐高清排行榜
8921010333	少女时代 Gee Japanese ver
.................

3.代码实现

object FenCi {
  def main(args: Array[String]): Unit = {
    val spark: SparkSession = SparkSession.builder()
      .master("local[1]")
      .appName("FenCi")
      .config("spark.seriailzer", "org.apache.spark.serializer.KryoSerializer")
      .getOrCreate()
    val sc = spark.sparkContext

    // source
    val in_file = sc.textFile("music.small")
    // 把每一行变成array
    val sourceRDD: RDD[Array[String]] = in_file.mapPartitions(iter => {
      iter.map(x => {
        x.trim.split("\t")
      })
    })
//    sourceRDD.take(10).foreach(x => x.foreach(println))
    /**结果
     * 8920408333
     * Locked Out Of Heaven 音乐高清视频MV
     * 8920422333
     * 影视-心上人啊快给我力量KTV(电影《神圣的使命》插曲
     * 8920491333
     * 068_奥特曼
     * 8920492333
     * 影视-幸福不会从天降KTV(电影《我们村里的年轻人》插
     * 8920527333
     * 邓紫棋 GEM 2013 X.X.X. LIVE 演唱会 【全场高清】
     * 8920529333
     * 067_外婆的澎湖湾
     * 8920588333
     * 卓依婷-纸飞机
     * 8920622333
     * 073_小红帽儿歌
     * 8920623333
     * 072_字母歌
     */
      // 取数组的第二个元素 内容
    val contentRDD = sourceRDD.mapPartitions(iter => {
      iter.map(x => {
        x(1).trim
      })
    })
//    contentRDD.take(10).foreach(println)
    /**
     * 王铮亮 时间都去哪了 《私人订制》插曲
     * Locked Out Of Heaven 音乐高清视频MV
     * 影视-心上人啊快给我力量KTV(电影《神圣的使命》插曲
     * 068_奥特曼
     * 影视-幸福不会从天降KTV(电影《我们村里的年轻人》插
     * 邓紫棋 GEM 2013 X.X.X. LIVE 演唱会 【全场高清】
     * 067_外婆的澎湖湾
     * 卓依婷-纸飞机
     * 073_小红帽儿歌
     * 072_字母歌
     */
      // 分词
    val fenciRDD = contentRDD.mapPartitions(iter => {
      iter.map(x => {
        fenci_func(x)
      })
    })

//    fenciRDD.take(10).foreach(println)

    /**
     * [影视-心上人啊快给我力量KTV(电影《神圣的使命》插曲, 影视, 心上人, 快给我, 力量, KTV, 电影, 神圣, 使命, 插曲]
     * [068_奥特曼, 068, 奥特曼]
     * [影视-幸福不会从天降KTV(电影《我们村里的年轻人》插, 影视, 幸福, 不会, 天降, KTV, 电影, 我们, 村里, 年轻人]
     * [邓紫棋 GEM 2013 X.X.X. LIVE 演唱会 【全场高清】, 邓紫棋, GEM, 2013, . , LIVE, 演唱会,  【, 全场, 高清]
     * [067_外婆的澎湖湾, 067, 外婆, 澎湖湾]
     * [卓依婷-纸飞机, 卓依婷, 纸飞机]
     * [073_小红帽儿歌, 073, 小红帽, 儿歌]
     * [072_字母歌, 072, 字母]
     */

      // 输出格式整理
    val resultRDD = fenciRDD.mapPartitions(iter => {
      iter.map(x => {
        val sb = new StringBuilder
        sb.append("\t").append(x)
      })
    })
    resultRDD.take(10).foreach(println)

    /**
     * [王铮亮 时间都去哪了 《私人订制》插曲, 铮亮, 时间,  《, 私人, 订制, 插曲]
     * [Locked Out Of Heaven 音乐高清视频MV, Locked, Out, Of, Heaven, 音乐, 高清, 视频, MV]
     * [影视-心上人啊快给我力量KTV(电影《神圣的使命》插曲, 影视, 心上人, 快给我, 力量, KTV, 电影, 神圣, 使命, 插曲]
     * [068_奥特曼, 068, 奥特曼]
     * [影视-幸福不会从天降KTV(电影《我们村里的年轻人》插, 影视, 幸福, 不会, 天降, KTV, 电影, 我们, 村里, 年轻人]
     * [邓紫棋 GEM 2013 X.X.X. LIVE 演唱会 【全场高清】, 邓紫棋, GEM, 2013, . , LIVE, 演唱会,  【, 全场, 高清]
     * [067_外婆的澎湖湾, 067, 外婆, 澎湖湾]
     * [卓依婷-纸飞机, 卓依婷, 纸飞机]
     * [073_小红帽儿歌, 073, 小红帽, 儿歌]
     * [072_字母歌, 072, 字母]
     */
    sc.stop()
  }

  def f1(x:String):Array[String] = {
    x.trim.split(" ")
  }

  /**
   * 分词方法
   * @param x
   * @return
   */
  def fenci_func(x:String): util.ArrayList[String] = {
    import scala.collection.JavaConversions._
    val word_list = new JiebaSegmenter().sentenceProcess(x.trim)
    val ls = new util.ArrayList[String]()
    ls.add(x.trim)
    word_list.foreach(word => {
      if (word.length > 1) {
        ls.add(word)
      }
    })
    ls
  }
}

4.总结

 /**
      * 使用process()结果是列表套列表,里面的每个小列表中元素依次是
      * [分好的词, 分好的词的第一个字符在文本字符数组的索引, 分好的词的最后一个字符在文本字符数组的索引的下一个索引]
      * INDEX:精准的切开,用于对用户查询词分词;
      * SEARCH:长词再切分,提高召回率。
      * 可以看到核心在于:
      * 1、内部包含一个字典
      * 2、分词逻辑
      * 3、不同模式的切分粒度
      */
    val str = "北京大学生活动中心"
    val ss = new JiebaSegmenter().sentenceProcess(str).toString
    // [北京, 大学生, 活动中心]
    val ss2 = new JiebaSegmenter().process(str, SegMode.INDEX).toString
    // [[北京, 0, 2], [大学, 2, 4], [学生, 3, 5], [大学生, 2, 5], [活动, 5, 7], [中心, 7, 9], [活动中心, 5, 9]]
    val ss3 = new JiebaSegmenter().process(str, SegMode.SEARCH).toString
    // [[北京, 0, 2], [大学生, 2, 5], [活动中心, 5, 9]]