RDD综合应用

RDD综合应用

实验目的

深入理解和掌握RDD各种常见操作和编程方法;掌握使用RDD编程解决实际问题的方法;掌握RDD分区映射编程方法;掌握RDD算法优化方法。

实验要求

  1. 掌握基于Maven的Scala和Spark Core编程环境配置;
  2. 掌握Scala容器方法编程;
  3. 掌握RDD数据分区;
  4. 掌握RDD算法优化方法。

实验内容

  1. 配置Scala和Spark Core编程环境
  2. 将文件TestHTML.txt上传至HDFS
  3. 文件TestHTML.txt中每一行为一个HTML网页,原本的HTML中原本的\r\n被替换成了\t,每个网页爬取自同一个网站的同一个栏目,布局基本相同;设计正则表达式提取每个网页的URL、标题和正文。
  4. 编写代码,实现以下功能:

(1) 将TestHTML.txt按行读取至RDD;

val rdd = sc.textFile("hdfs://主机名或ip地址:端口号/文件路径")  

(2) 使用正则表达式提取每篇网页的URL、标题和文章正文(训练正则表达式+5分),RDD类型RDD[String]各元素为:
RDD元素:标题+文章正文
或URL和去除了HTML标记的所有文本:
RDD元素:去除了HTML标记的所有文本
提示:去除HTML标记,用正则表达式<[^>]*>将HTML替换为空字符串即可

val textWithoutTag = "<[^>]*>".r replaceAllIn(html, "")
val url_pattern = "(https.*?shtml)".r  
val title_pattern = "<h1 class=.*?>(.*?)</h1>".r  
val content_pattern = "<p cms-style=.*?>(.*?)</p>".r  	  
val url = rdd.map(x=>url_pattern.findFirstMatchIn(x).map(_.group(1).replaceAll("\t", "")).mkString(""))  
val title = rdd.map(x=>title_pattern.findFirstMatchIn(x).map(_.group(1)).mkString(""))  
val content = rdd.map(x=>content_pattern.findAllMatchIn(x).map(_.group(1).replaceAll("  ", "").replaceAll("[\\u3002\\uff1b\\uff0c\\uff1a\\u201c\\u201d\\uff08\\uff09\\u3001\\uff1f\\u300a\\u300b]","").replaceAll("<.*?>", "")).toList)  

以上代码是分别提取链接,标题和正文内容,其中提取正文时,打算将标点符号,标签一节空格全部替换掉,但仍有福分表点符号保留

Regx:[\u3002\uff1b\uff0c\uff1a\u201c\u201d\uff08\uff09\u3001\uff1f\u300a\u300b]
在软件中的演示结果
在这里插入图片描述

但不知为何保存的文件中仍能看到一些标点符号,尤其是“, ”为之后最终查询结果埋下了伏笔。最终,将提取到url,标题和正文保存为一个三元组
保存结果预览
在这里插入图片描述

(3) 对所有文本执行中文分词,统计各文章中的词汇次数,RDD类型RDD[List[(String, Int)]]

val wordsRdd = url_title_content.map(x=>{
val words = new JiebaSegmenter().sentenceProcess(x._2.toString()+x._3.toString()).toArray()
words
})
val wordsIntexts = wordsRdd.map(x=>({val wordcount = x.map(y=>(y, 1)).groupBy(_._1).map(y=>(y._1.toString, y._2.count(z=>true)))
wordcount.toList.sortBy(_._2)
}))

RDD元素:List((词汇, 词汇在文章中的次数))
在这里插入图片描述

(4) 将RDD元素转为词汇、词汇文章次数二元组,RDD类型RDD[(String, Int)]
RDD元素:(词汇, 词汇在文章中的次数)
提示:flatMap

val wordsIntexts_flatMap = wordsIntexts.flatMap(x=>x).sortBy(_._2)  

在这里插入图片描述
(5) 计算词汇的总次数并保存为文件,RDD[(String, Int)]
RDD元素:(词汇, 词汇总次数)
提示:reduceByKey或groupByKey+Scala容器方法

val wordsIntexts_sum = wordsIntexts_flatMap.reduceByKey(+).sortBy(_._2)
wordsIntexts_sum.saveAsTextFile(“hdfs://主机名或ip地址:端口号/文件路径”)

在这里插入图片描述

  1. 代码优化
  • shuffle优化
    使用mapPartiton优化分词和词频统计(参考PPT第4章3.1.2例2)
    提示:
//先将RDD类型转为RDD[(String. Int)],  mapPartition不能修改元素类型
rdd.map(x => (x, 1)) 
//遍历4(2)步生成的RDD的各分区,将4(3)~4(5)步的shuffle算子在分区内实现
//得到各个分区的词汇和总数
//再用一个reduceByKey算子将各分区的词汇和总数汇总
.mapPartition(x => {
			//实例化jieba类,一个分区实例化一次,而不是map中一个元素实例化一次
val jieba = new JiebaSegmenter()
//遍历各分区中的元素,按4(3)步要求执行分词
//x类型为Iterator,必须先转为List才能应用容器方法
val words = x.toList.map(y => jieba.sentenceProcess(y._1).toArray())
//使用scala容器方法计算后续操作
//在此(分区内)执行的所有操作均不会产生shuffle
……
//计算完毕后,再将数据构造成Iterator[(String, Int)]类型返回
//mapPartition不能修改元素类型,返回值类型必须与参数x的类型一致
. toIterator
})
  • 存储结构优化
    采用更为高效的Object文件保存RDD
  1. 编译代码输出jar包并上传并spark-submit运行
    先使用小规模数据集在本地调试,再进入曙光大数据系统使用大数据集运行(参考《曙光大数据系统使用方法》)
    在这里插入图片描述
spark-submit --class 项目路径.项目名 --master yarn /文件路径/jar包名

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思考:分别执行优化前和优化后的代码,并记录其运行时间和保存文件的大小

  1. 进入spark-shell,将第4步保存的Object文件读取到RDD(参考PPT第4章1.5),使用filter算子查询词汇信息:
    读取ObjectFile
val objFile = sc.objectFile[(String, Int)]("hdfs://HA/文件路径")  

在这里插入图片描述
在这里插入图片描述

(1) 查询"量子"并输出其次数

val quantum = objFile.filter(x=>(x._1=="量子")).map(_._2).collect()  

在这里插入图片描述

(2) 查询"核电站"并输出其次数

val nuclear = objFile.filter(x=>(x._1=="核电站")).map(_._2).collect()  

在这里插入图片描述

(3) 查询次数最多的二字词汇并输出(直接collect)

val max_num = objFile.filter(x=>x._1.length==2).map(_._2).max()  
val result = objFile.filter(x=>x._1.length==2 && x._2 == max_num).collect() 

在这里插入图片描述

代码

package cn.edu.swpu.scs
import com.huaban.analysis.jieba.JiebaSegmenter
import org.apache.spark.{SparkConf, SparkContext}
object Jieba {
def main(args: Array[String]):Unit = {
 val conf = new SparkConf().setAppName("名字")  
 val sc = new SparkContext(conf)  
val rdd = sc.textFile("hdfs://HA/文件路径")  
val url_pattern = "(https.*?shtml)".r  
val title_pattern = "<h1 class=.*?>(.*?)</h1>".r  
val content_pattern = "<p cms-style=.*?>(.*?)</p>".r  
val url = rdd.map(x => url_pattern.findFirstMatchIn(x).map(_.group(1).replaceAll("\t", "")).mkString(""))  
val title = rdd.map(x => title_pattern.findFirstMatchIn(x).map(_.group(1)).mkString(""))  
val content = rdd.map(x => content_pattern.findAllMatchIn(x).map(_.group(1).replaceAll("  ", "").replaceAll("[\\u3002\\uff1b\\uff0c\\uff1a\\u201c\\u201d\\uff08\\uff09\\u3001\\uff1f\\u300a\\u300b]", "").replaceAll("<.*?>", "")).toList)  
val url_title_content = rdd.map(x => ((url_pattern.findFirstMatchIn(x).map(_.group(1).replaceAll("\t", "")).mkString("")), (title_pattern.findFirstMatchIn(x).map(_.group(1)).mkString("")), (content_pattern.findAllMatchIn(x).map(_.group(1).replaceAll("  ", "").replaceAll("<.*?>", "")).toList))) //提取url,标题,正文内容构成三元组  
val wordsRdd = url_title_content.map(x => {  
  val words = new JiebaSegmenter().sentenceProcess(x._2.toString() + x._3.toString()).toArray()  
  words  
})  
//词汇在各文章的次数  
val wordsIntexts = wordsRdd.map(x => ({  
  val wordcount = x.map(y => (y, 1)).groupBy(_._1).map(y => (y._1.toString, y._2.count(z => true)))  
  wordcount.toList.sortBy(_._2)  
}))  
//词汇在各文章的次数的二元组  
val wordsIntexts_flatMap = wordsIntexts.flatMap(x => x).sortBy(_._2)  
//词汇在所有文章中的总次数  
val wordsIntexts_sum = wordsIntexts_flatMap.reduceByKey(_ + _).sortBy(_._2)  
wordsIntexts_sum.saveAsObjectFile("hdfs://HA/文件路径")  
}
}
// //应用
// val objFile = sc.objectFile(String, Int) //读取ObjectFile
// val quantum = objFile.filter(x=>(x.1==“量子”)).map(._2).collect()
// val nuclear = objFile.filter(x=>(x.1==“核电站”)).map(._2).collect()
//
// val max_num = objFile.filter(x=>x.1.length==3).map(._2).max()
// val result = objFile.filter(x=>x._1.length==3 && x._2 == max_num).collect()

实验感悟

因为本次需要处理的过程比较复杂,无法用一个语句完成,所以开始尝试函数式编程。然后又熟悉了一下在大数据曙光平台上的操作。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值