关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
前几天优化了阅读计数模式,现在就可以拿数据来统计分析。有了明细阅读数据,可以分析出哪些博文相对比较热门、阅读变化等等。我就简单分析一下前7天的数据。
前7天的每天阅读量数据可以统计并展示出来,这个用图表展示比较清晰。对比了一些js的图表插件,决定使用HighChart.js,因为这个图表数据可以用json加载,帮助文档也很完善,使用简单方便。具体帮助可以参考HighChart中文网。先给大家看看图表最终效果图:
8月11号之前是没有数据的,因为8月11号才上线阅读明细记录的功能(具体细节参考博文《我的网站搭建(第24天) 阅读计数优化》)。把前7天的数据制作成折线图,方便分析趋势变化。
由于HighChart.js使用到的数据是json,所以这里我打算用ajax去获取数据,再加载图表。
为了讲清楚(也为了应付没有看第24天的那篇博文),先交待清楚一些东西。我那个阅读计数应用叫view_record,记录明细的模型是Recorder。具体模型如下:
#coding:utf-8 from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.auth.models import User class Recorder(models.Model): """阅读明细记录""" #ContentType关联字段 content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey( ct_field="content_type", fk_field="object_id" ) #普通字段 ip_address = models.CharField(max_length=15) user = models.ForeignKey(User, blank=True, null=True) view_time = models.DateTimeField(auto_now=True)
下面会用得比较多的是 view_time 日期时间字段,主要围绕它统计分析。
打开该应用的view.py文件,写入如下代码:
#coding:utf-8 from django.http import HttpResponse from django.contrib.contenttypes.models import ContentType from view_record.models import Recorder from blog.models import Blog import datetime import json def get_seven_days_data(request): """获取7天内的数据""" #得到7天的日期 now = datetime.datetime.now() #now还包含小时,分钟等,需要去掉 end_date = datetime.datetime(now.year, now.month, now.day, 0, 0) start_date = end_date - datetime.timedelta(7) days = map(lambda x: end_date - datetime.timedelta(x), range(7, 0, -1)) #得到前7天的阅读量 oneday = datetime.timedelta(1) obj_type = ContentType.objects.get_for_model(Blog) counts = map(lambda x: Recorder.objects.filter(content_type = obj_type, view_time__range=(x,x + oneday)).count(), days) #获取HighChart图表设置 chart = {} #图表设置 chart['chart'] = {"type":"line", #设置为折线图 "borderColor": '#dfdfdf', #设置边框 "borderWidth": 1, "borderRadius":5, "margin": 35, "marginBottom":70 } #去掉图表标题,太大不好看 chart['title'] = {"text":""} #X轴设置 chart['xAxis'] = {"categories": map(lambda x: datetime.datetime.strftime(x, '%m-%d'), days), "tickmarkPlacement": 'on', "title":{ "enabled":True, "text":u"前7日阅读量变化" }} #Y轴(不写标题,隐藏Y轴) chart['yAxis'] = {"title":{"text":""}, "labels":{"enabled":False}} #数据标签 chart['plotOptions'] = {"line":{"dataLabels":{"enabled":True}}, "enableMouseTracking": False} #数据系列 chart['series'] = [{"name":"view nums","data":counts}] #图例不显示 chart['legend'] = {"enabled":False} #右下角版权不显示 chart['credits'] = {"enabled":False, "text":"yshblog.com","href":"http://yshblog.com/"} return HttpResponse(json.dumps(chart), content_type="application/json")
这里的图表相关设置我就不说了,自己看HighChart.js的帮助。此处重点是获取前7天的日期和对应的数据。
日期使用datetime加减得到7天的日期。Recorder筛选需要两个条件,一个是ContentType处理;另一个是日期的范围。这个需要在view_time后面加上__range,并指定范围即可。
再加入url路由设置:
from django.conf.urls import include, url #http://localhost:8000/view_record/ #start with 'view_record/' urlpatterns = [ url(r'^get_seven_days_data$','view_record.views.get_seven_days_data',name='get_seven_days_data'), ]
总路由再添加应用的url路由:
urlpatterns = [ #...其他路径设置就不显示出来了 url(r'^view_record/',include('view_record.urls')), ]
接着,修改前端页面显示图表。我把图表放在首页,所以打开对应的首页模版文件index.html(你就根据自己的情况修改),加入如下代码:
{#我所有模版都会基于一个最底层的base.html文件#} {% extends "base.html" %} {#此处是head头部分的拓展,加入HighChart.js的引用。我的base.html已经引用了jQuery#} {% block extra_head %} <script src="/static/js/highcharts.js"></script> {% endblock %} {% block content %} {#这里还有其他内容我就没显示,此处是body部分#} {#加入HighChart的容器#} <div id="container" style="height:300px"></div> {% endblock %} {#该block是body部分的底部拓展,通常我是用来写js代码#} {% block extra_footer %} <script type="text/javascript"> $.ajax({ type:"GET", url:"{% url 'get_seven_days_data' %}", cache:false, dataType:'text', success:function(result){ //加载图表 $('#container').highcharts(JSON.parse(result)); }, error:function(XMLHttpRequest, textStatus, errorThrown){ //alert(textStatus); } }); </script> {% endblock %}
保存,重启服务,就可以看到前面截图的效果了。
若单单只有这个图表,显得有点空。所以我寻思在这个基础上加一些文字分析说明并显示出来。
这些文字分析说明就一并通过这个ajax获取和显示到前端页面中。json数据结构就需要改动成如下:
{ "chart" : {图表设置}, "texts" : [文字分析数组] }
文字分析说明主要分析了阅读频率、阅读时间段、点击最多的博文等等。代码有点多,我还是一并贴出来。重点和难点是对模型的统计查询。若该部分弄不明白也可以使用SQL语句得到原始的统计查询。
#coding:utf-8 from django.http import HttpResponse from django.core.urlresolvers import reverse from django.contrib.contenttypes.models import ContentType from django.db.models import Count, Max #Django模型统计函数 from view_record.models import Recorder from blog.models import Blog import datetime, json def get_seven_days_data(request): """获取7天内的数据""" #得到7天的日期 now = datetime.datetime.now() end_date = datetime.datetime(now.year, now.month, now.day, 0, 0) start_date = end_date - datetime.timedelta(7) days = map(lambda x: end_date - datetime.timedelta(x), range(7, 0, -1)) #得到前7天的阅读量 oneday = datetime.timedelta(1) obj_type = ContentType.objects.get_for_model(Blog) counts = map(lambda x: Recorder.objects.filter(content_type = obj_type, view_time__range=(x,x + oneday)).count(), days) #7天内的Blog阅读全部明细 seven_data = Recorder.objects.filter(content_type = obj_type, view_time__range=(start_date, end_date)) data = {} data["chart"] = _get_chart_data(days, counts) data['texts'] = _get_texts_data(days, counts, seven_data) return HttpResponse(json.dumps(data), content_type="application/json") def _get_chart_data(days, counts): """get the chart json data""" chart = {} #图表设置 chart['chart'] = {"type":"line", "borderColor": '#dfdfdf', "borderWidth": 1, "borderRadius":5, "margin": 35, "marginBottom":70 } #标题 chart['title'] = {"text":""} #X轴 chart['xAxis'] = {"categories": map(lambda x: datetime.datetime.strftime(x, '%m-%d'), days), "tickmarkPlacement": 'on', "title":{ "enabled":True, "text":u"前7日阅读量变化" }} #Y轴(不写标题,隐藏Y轴) chart['yAxis'] = {"title":{"text":""}, "labels":{"enabled":False}} #数据标签 chart['plotOptions'] = {"line":{"dataLabels":{"enabled":True}}, "enableMouseTracking": False} #数据系列 chart['series'] = [{"name":"view nums","data":counts}] #图例 chart['legend'] = {"enabled":False} #右下角版权 chart['credits'] = {"enabled":False, "text":"yshblog.com","href":"http://yshblog.com/"} return chart def _get_texts_data(days, counts, seven_data): """get analysis texts""" texts = [] #总点击数 __viewed_num(seven_data, texts) #时间段分析 __anaylsis_hour(seven_data, texts) #周末和工作日分析 __anaylsis_week(days, counts, texts) #阅读最多的博文 __max_viewed(seven_data, texts) return texts def __viewed_num(seven_data, texts): viewed_count = seven_data.count() texts.append(u'前7日总阅读%s次,平均 %.2f次/天' % (viewed_count, viewed_count/7.)) if viewed_count == 0: texts.append(u'桑心!居然一次都没有,我要好好分析是什么原因 T_T') elif viewed_count <= 7*2: texts.append(u'好吧,阅读量有点少,可能宣传不够或者我最近博文写少了') elif viewed_count <= 7*5: texts.append(u'目前来说,还需努力,继续写出好的文章') elif viewed_count <= 7*8: texts.append(u'^_^ 朋友,若您觉得不错,帮忙宣传一下呗') else: texts.append(u'再接再厉,把我的博客弄得更好!') def __anaylsis_hour(seven_data, texts): hour_range = [(0,5), (5,8), (8,12), (12,14), (14,18), (18,24)] hour_viewed_num = map(lambda x: seven_data.filter(view_time__hour__range=x).count(), hour_range) #获取最大值 max_num = max(hour_viewed_num) #判断最大值位置 if max_num == 0: texts.append(u'没有被阅读,无法统计哪个时间段阅读次数最多') else: for i, value in enumerate(hour_viewed_num): if max_num == value: hour_range_item = hour_range[i] if hour_range_item == (0,5): texts.append(u'凌晨0点到5点阅读最多,都是苦比的程序猿吗') elif hour_range_item == (5,8): texts.append(u'早上5点到8点阅读最多,我还在睡觉,时区不同吗') elif hour_range_item == (8,12): texts.append(u'早上8到12点阅读最多,正常工作时间') elif hour_range_item == (12,14): texts.append(u'中午12点到14点阅读最多,不用午休吗') elif hour_range_item == (14,18): texts.append(u'下午14点到19点阅读最多,正常工作时间') elif hour_range_item == (18,24): texts.append(u'晚上19点到24点阅读最多,要么自学要么加班中') def __anaylsis_week(days, counts, texts): week_day_nums = 0 work_day_nums = 0 #获取周末和工作日的阅读量 for i, value in enumerate(days): if value.isoweekday()>5: week_day_nums += counts[i] else: work_day_nums += counts[i] texts.append(u'工作日阅读量:%s,周末阅读量:%s' % (work_day_nums, week_day_nums)) #分析阅读量 avg_week = week_day_nums/2. avg_work = work_day_nums/5. if avg_work > avg_week: text = u'工作日平均%.2f次/天,周末平均%.2f次/天。大部分工作中学习' % (avg_work, avg_week) elif avg_work < avg_week: text = u'工作日平均%.2f次/天,周末平均%.2f次/天。大部分人挺宅的' % (avg_work, avg_week) else: if avg_work == 0: text = u'什么数据都没有,无法分析工作日和周末情况' else: text = u'工作日平均%.2f次/天,周末平均%.2f次/天。难得平均数据一样' % (avg_work, avg_week) texts.append(text) def __max_viewed(seven_data, texts): #对object_id分组计数,并按计数倒序排列。结果是一个数组字典 #获取最多点击次数的博文,这里弄不明白就用SQL语句查询 qs_count = seven_data.values("object_id").annotate(Count('id')).order_by('-id__count') count_num = qs_count.count() if count_num>0: blog_id = qs_count[0]['object_id'] #得到数量最大的id blog = Blog.objects.get(id = blog_id) args = [blog_id] texts.append(u'点击最多:<a href="%s" target=_blank>%s</a>' % (reverse('detailblog', args=args), blog.caption)) if count_num>1: blog_id = qs_count[1]['object_id'] #得到数量次多的id blog = Blog.objects.get(id = blog_id) args = [blog_id] texts.append(u'点击第二:<a href="%s" target=_blank>%s</a>' % (reverse('detailblog', args=args), blog.caption))
代码有点多,大家选择性看看就行。里面有几个地方需要注意的:
1、__anaylsis_hour方法中的filter筛选器使用到条件view_time__hour__range=x,view_time是日期时间字段,__hour是得到该字段值中的小时,__range是范围。组合起来则是得到指定小时范围的记录。
2、__max_viewed方法中的分组计数统计,seven_data.values("object_id").annotate(Count('id')),即得到对object_id字段分组,并对id字段计数。结果是得到一个查询字典,再排序。具体查询统计可以参考一下官方文档。
修改了后台方法之后,还需要对应修改一下前端页面:
{#我所有模版都会基于一个最底层的base.html文件#} {% extends "base.html" %} {#此处是head头部分的拓展,加入HighChart.js的引用。我的base.html已经引用了jQuery#} {% block extra_head %} <script src="/static/js/highcharts.js"></script> {% endblock %} {% block content %} {#这里还有其他内容我就没显示,此处是body部分#} <div class="row"> <div class="col-xs-12 col-md-5"> <div class="panel panel-default"> <div class="panel-heading"> <span>前7日阅读分析</span> </div> <div class="panel-body" style="height:255px"> {#加一个ul作为容器可被添加文字#} <ul id="analysis"></ul> </div> </div> </div> <div class="col-xs-12 col-md-6"> <div id="container" style="height:300px"></div> </div> </div> {% endblock %} {#该block是body部分的底部拓展,通常我是用来写js代码#} {% block extra_footer %} <script type="text/javascript"> $.ajax({ type:"GET", url:"{% url 'get_seven_days_data' %}", cache:false, dataType:'text', success:function(result){ data = JSON.parse(result); //加载图表 $('#container').highcharts(data['chart']); //加载文字 texts = data['texts']; if(texts.length > 0){ $.each(texts, function(i, item){ $('#analysis').append("<li>" + (i+1) + "、" + item + "</li>"); }); }else{ $('#analysis').append("<li>暂无相关分析</li>"); } }, error:function(XMLHttpRequest, textStatus, errorThrown){ //alert(textStatus); } }); </script> {% endblock %}
这样就可以简单实现图表和文字分析了,效果如下:
当然,更强大的方法是使用机器学习进一步挖掘数据和分析数据。
dfjk59@126.com
test😀
2018-04-17 11:31 回复
dfjk59@126.com
test
2018-04-17 11:31 回复
dfjk59@126.com 回复 dfjk59@126.com
😁
2018-04-17 11:32 回复