实际的业务中,我们要统计访问量,必然是统计IP字段,但是拿到的数据可能是这样子的:
"27.38.5.159" "-" "31/Aug/2015:00:04:37 +0800" "GET /course/view.php?id=27 HTTP/1.1" "303" "440" - "http://www.ibeifeng.com/user.php?act=mycourse" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "-" "learn.ibeifeng.com"
"27.38.5.159" "-" "31/Aug/2015:00:04:53 +0800" "GET /theme/image.php/bootstrap/core/1427679483/i/grades HTTP/1.1" "200" "912" - "http://learn.ibeifeng.com/course/view.php?id=27" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "-" "learn.ibeifeng.com"
"219.135.107.124" "-" "31/Aug/2015:02:13:56 +0800" "GET /mod/page/view.php?id=11152§ion=4 HTTP/1.1" "200" "9784" - "http://learn.ibeifeng.com/mod/page/view.php?id=11154§ion=4" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10240 QQBrowser/8.3.4769.400" "-" "learn.ibeifeng.com"
"60.30.29.146" "-" "31/Aug/2015:07:40:26 +0800" "GET /course/view.php?id=16 HTTP/1.1" "303" "440" - "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36" "-" "learn.ibeifeng.com"
观察发现,如果要切分,我们可以使用空格把每个字段且分开来,从而得到IP,不错实际得到的IP字段如下:
"27.38.5.159"
"27.38.5.159"
"219.135.107.124"
"60.30.29.146"
IP是出来了,但是每个IP会有双引号,这显然不是我们想要的这样:
27.38.5.159
27.38.5.159
219.135.107.124
60.30.29.146
怎样做到呢?
有人想到用substring()进行截取,代码如下:
val ip = strings(0).substring(1, 12)
但是问题又来了,第一条数据和第二条数据位数和格式上一样子的,IP是从1-12,没有问题,但是第三条第四条数据显然不适用了,第三条的IP超出了12,这一来,我们得到的IP就不全:如下:
27.38.5.159
27.38.5.159
219.135.107
60.30.29.14
看到,IP不去全了,怎么解决?
在我的灵机一动下,想到了如下的解决办法?
还是同样的方法,但是end位置变一下:
val ip = strings(0).substring(1, strings(0).length-1)
将substring(1,12)中,end换成(strings(0).length-1),这样是根据截取的字符的下标通过length-1获取最后一位,做的好处就是不管有多少位,都会动态的获取最后一位下表,并截取出来。
如此,解决这样的问题,但是我们可发现,利用下标,效率会慢很多,但是能够解决问题处理少量的数据,还是可以的。
完整的代码如下:
package spark.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import spark.core.TimeUtil.{NumberUtil, TimeRangeUtil}
/**
* @className: spark.core.WebLOgAndLysis
* @author: Star
* @create: 2022-07-21 16:15
* Description:
* 用户访问量的TOP10;
* 以半小时为单位统计用户的人们访问时段。
*/
object WebLOgAndLysis {
/**
* 数据清洗和字段提取;
*
* @param rdd 原始的日志数据
* @return 清洗后的RDD(ip,dt,statue)
*/
def extract(rdd: RDD[String]): RDD[(String, String, String)] = {
rdd.map(line => {
val strings = line.split(" ")
val ip = strings(0).substring(1, strings(0).length - 1)
val time = strings(2).substring(13, 18)
val dt = TimeRangeUtil.toHalfHourRange(time)
val statue = strings(7).substring(1, 4)
(ip, dt, statue)
})
.filter(_._3.equals("200"))
}
def main(args: Array[String]): Unit = {
//ETL操作
//程序入口
val conf = new SparkConf()
.setMaster("local[2]")
.setAppName("WebLOgAndLysis")
val sc = new SparkContext(conf)
//读取文件
val rdd = sc.textFile("G:\\MyWoekSpease\\hadoop-test\\moodle.access.log")
//调用extract传入文件
val extractedRDD: RDD[(String, String, String)] = extract(rdd)
extractedRDD.foreach(println(_))
}
}
封装的处理时间的方法:
package spark.core.TimeUtil
/**
* @className: spark.core.TimeUtil.TimeRangeUtil
* @author: Star
* @create: 2022-07-21 16:40
*/
object TimeRangeUtil {
def toHalfHourRange(str: String): String = {
val strings = str.split(":")
var minute = strings(1)
minute = if (minute.toInt >= 30) "30" else "00"
strings(0) + ":" + minute
}
}
至此,欢迎更好的想法来讨论。