PV、UV分析常用于MapReduce的离线日志分析系统中,那么如果用Spark该怎么做呢?本文仅对数据进行统计进行分析,权当抛砖引玉,希望能看到大佬对其进行更深入的操作。
1、需求
根据数据计算各网站的PV,UV,同时,只显示top5(按PV值,或者UV值排序,取前5名)
2、 数据
数据是对13w+条PV、UV记录,数据的格式如下,列之间用tab
分隔,如果读者感兴趣,可以点击这里下载
。
57.222.102.178 青海 2018-11-12 1542011090255 167000459809553480 www.baidu.com View
57.222.102.178 青海 2018-11-12 1542011090255 167000459809553480 www.gome.com.cn View
57.222.102.178 青海 2018-11-12 1542011090255 167000459809553480 www.taobao.com Buy
103.190.42.13 江苏 2018-11-12 1542011090255 4152752257070526126 www.baidu.com View
103.190.42.13 江苏 2018-11-12 1542011090255 4152752257070526126 www.suning.com Comment
103.190.42.13 江苏 2018-11-12 1542011090255 4152752257070526126 www.taobao.com Buy
103.190.42.13 江苏 2018-11-12 1542011090255 4152752257070526126 www.gome.com.cn Buy
103.190.42.13 江苏 2018-11-12 1542011090255 4152752257070526126 www.gome.com.cn Login
3、PV分析
3.1 期望得到的元素
首先我们来分析PV,我们要明确需要使用哪一列来作为我们统计的依据,既然要统计PV,那么肯定是按网址来统计,所以我们取第五列数据作为统计依据。
//读取源文件,并设置最小分区数为5(这个最小分区数是随意设的,为了提高并行度,不必纠结)
val file: RDD[String] = sc.textFile("./pvuvdata",5)
//将数据进行切割,获取第5列数据,并将其转换成("www.baidu.com",1)这样的键值对
val pair: RDD[(String, Int)] = file.map(line=> (line.split("\t")(5),1) )
3.2 期望统计排序的元素
我们已经得到了作为统计依据的列,现在我们需要对PV进行统计和排序,统计很好理解,就是pair.reduceByKey(_+_)
,但是我们要如何对value进行排序呢,Spark中并没有reduceByvalue()
函数,这时我们可以转换一下思维(有经验的同学可能知道可以用sorted()
函数,我们这里先不讨论),Spark中有一个SortedBykey()
函数,可以直接对key进行全局排序,如果我们将(“www.baidu.com,1”)转换成(1,“www.baidu.com”),不就可以直接排序了吗?
val map: RDD[(Int, String)] = reduce.map(_.swap)
val sorted: RDD[(Int, String)] = map.sortByKey(false)
3.3 整理并打印
得到了全排序的结果,结果是(9999,“www.baidu.com”)格式的,我们需要将其转换回(“www.baidu.com”,9999)的格式,然后取前5条打印即可。
val res: RDD[(String, Int)] = sorted.map(_.swap)
val pv: Array[(String, Int)] = res.take(5)
pv.foreach(println)
3.4 PV分析代码
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local").setAppName("sort")
val sc = new SparkContext(conf)
sc.setLogLevel("ERROR")
val file: RDD[String] = sc.textFile("bigdata-spark/data/pvuvdata",5)
// 187.144.73.116 浙江 2018-11-12 1542011090255 3079709729743411785 www.jd.com Comment
println("----------PV:-----------")
val pair: RDD[(String, Int)] = file.map(line=> (line.split("\t")(5),1) )
val reduce: RDD[(String, Int)] = pair.reduceByKey(_+_)
val map: RDD[(Int, String)] = reduce.map(_.swap)
val sorted: RDD[(Int, String)] = map.sortByKey(false)
val res: RDD[(String, Int)] = sorted.map(_.swap)
val pv: Array[(String, Int)] = res.take(5)
pv.foreach(println)
}
4、UV分析
4.1 期望得到的元素
UV统计方式大体上和PV相同,但是需要注意到一点,UV统计的是不同用户访问这个网站的数量,也就是说,同一个用户可能多次访问一个网站,但是最终访问这个网站的IP只有一个,所以我们需要将网址和IP绑定在一起,然后执行去重操作,这样得到的UV才是准确的。
// 187.144.73.116 浙江 2018-11-12 1542011090255 3079709729743411785 www.jd.com Comment
val keys: RDD[(String, String)] = file.map(
line => {
val strs: Array[String] = line.split("\t")
(strs(5), strs(0))
}
)
val key: RDD[(String, String)] = keys.distinct()
4.2 期望统计排序的元素
统计排序的时候,我们只需要统计网站就行,所以我们只取(“www.baidu”,192.168.126.10)这个键值对中的第一个元素,然后对它进行排序,这里我们使用sorted()
函数来进行排序,sorted()
函数可以传入一个参数,指定要进行排序的value的位置。
val pairx: RDD[(String, Int)] = key.map(k => (k._1,1))
val uvreduce: RDD[(String, Int)] = pairx.reduceByKey(_+_)
val uvSorted: RDD[(String, Int)] = uvreduce.sortBy(_._2,false)
4.3 整理并打印
现在我们也得到了UV全排序的结果,直接取前5条打印即可。
val uv: Array[(String, Int)] = uvSorted.take(5)
uv.foreach(println)
4.4 UV分析代码
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local").setAppName("sort")
val sc = new SparkContext(conf)
sc.setLogLevel("ERROR")
val file: RDD[String] = sc.textFile("bigdata-spark/data/pvuvdata",5)
// 187.144.73.116 浙江 2018-11-12 1542011090255 3079709729743411785 www.jd.com Comment
val keys: RDD[(String, String)] = file.map(
line => {
val strs: Array[String] = line.split("\t")
(strs(5), strs(0))
}
)
val key: RDD[(String, String)] = keys.distinct()
val pairx: RDD[(String, Int)] = key.map(k => (k._1,1))
val uvreduce: RDD[(String, Int)] = pairx.reduceByKey(_+_)
val uvSorted: RDD[(String, Int)] = uvreduce.sortBy(_._2,false)
val uv: Array[(String, Int)] = uvSorted.take(5)
uv.foreach(println)
}