关于本站
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 回复