图表是可以直观展示数据差异的一种形式,在生活中也有很多地方会应用到图表,最近在做吉他谱网站后台时,就打算做一个近十天网站浏览量折线图的功能,在捣鼓了一番后,实现的效果是下图所示,十天之前的不显示,x轴显示日期,y轴显示对应的浏览量,这里的浏览量指的是PV(page view)即页面的浏览量,图表使用的插件是ECharts,为了帮助有同样需要的小伙伴,所以就将实现的过程跟大家分享一下吧。

Flask使用ECharts实现网站浏览量统计图功能

实现过程

数据库部分

因为这篇文章是为了和大家分享网站统计图表的实现过程,所以为了方便表达先假设一个数据表Sitedata表

Flask使用ECharts实现网站浏览量统计图功能

有点类似于元数据表,不过这个表所属的对象只有一个,就是整个网站,这样设计表的原因是因为在后期动态添加网站属性会比较方便,而不用在表上添加新字段并迁移。

然后我们在初始化数据库后,要插入三条数据分别为:

datelog记录一个日期用来判断是否为今天,初始值为数据库初始化时当天的日期,字符串形式;

todayviews今日的浏览量,初始值为0,字符串形式;

viewslog储存时间段内日期与浏览量关系的json,类似'{"2020-07-18":11,"2020-07-19":22,"2020-07-20":33}',初始值为{}字符串形式。

代码部分

models.py

from datetime import date

class Sitedata(db.Model):
    __tablename__='sitedata'
    id=db.Column(db.Integer,primary_key=True)
    key=db.Column(db.String(128))
    value=db.Column(db.Text)
    comment=db.Column(db.Text)

    @staticmethod
    def insert_data():
        init_data={'datelog':[str(date.today()),'日期'],
              'todayviews':['0','今日浏览数']
              'viewlog':['{}','浏览统计']}
        for d in init_data:
            data=Sitedata.query.filter_by(key=d).first()
            if data is None:
                data=Sitedata(key=d)
            data.value=init_data[d][0]
            data.comment=init_data[d][1]
            db.session.add(data)
        db.session.commit()

在创建表之后,调用Sitedata.insert_data()方法插入初始数据。

路由部分

在这个项目中有两个蓝图一个是main,这个蓝图中的路由是所有用户可访问,另一个是admin,这个是管理蓝图,这里面的路由需要管理员登陆才能访问。

浏览量的计算通过flask中的before_request装饰器来实现,每当用户访问一次main蓝图中的路由时,执行被before_request装饰的函数,这也是整个功能最主要的部分。这里我就直接上代码吧,通过注释解释。

main.py

from models import Sitedata
import datetime
import time
import json

def str2date(string,f="%Y-%m-%d"):#写一个将字符串形式的时间转换成日期对象的函数
    year,month,day=time.strptime(string,f)[:3]
    date=datetime.date(year,month,day)
    return date

@main.before_request
def first():
    today=datetime.date.today()#获得今天的日期
    datelog=Sitedata.query.filter_by(key='datelog').first()#用来判断是否今天的日期
    todayviews=Sitedata.query.filter_by(key='todayviews').first()#今天的浏览量
    viewlog=Sitedata.query.filter_by(key='viewlog').first()#时间段中浏览量记录json

    if datelog.value !=str(today):#若到了第二天
        yesterday=datelog.value
        yesterday_views=todayviews.value

        viewlog_dict=json.loads(viewlog.value)#将json转换成dict的形式
        wait_to_pop=[]
        for d in viewlog_dict:
            date=str2date(d)
            if (today-date).days>10:#十天以上的记录删除
                wait_to_pop.append(d)
        for i in wait_to_pop:
            viewlog_dict.pop(i)
        viewlog_dict[yesterday]=int(yesterday_views)#在viewlog中添加昨天的浏览量记录

        viewlog.value=json.dumps(viewlog_dict)
        datelog.value=str(today)
        todayviews.value='1' #第二天的第一次访问所以为1
        db.session.add(viewlog)
        db.session.add(datelog)
        db.session.add(todayviews)
    else:#还在今天
        todayviews.value=str(int(todayviews.value)+1)
        db.session.add(todayviews)
    db.session.commit()

这个first()函数会在用户每次请求main蓝图中的路由时判断储存在数据库中的datelog是否为今天,如果还是今天,就在今天的浏览量todayviews上加一;如果到了第二天就将数据库中日期与浏览量记录为昨天昨天的浏览量,并添加到viewlog中,并将viewlog中十天以前的记录删除;然后重设数据库中日期datelog为第二天,重设todayviews为1(因为这也是一次请求,所以为1)。

上面就是在主蓝图中需要做的事了,而图表是显示在管理蓝图的主页中的,这一部分还是比较简单的,只是在数据库中取数据而已,所以我们接下来在admin蓝图实现一下显示图表的代码吧!

admin.py

from models import Sitedata
import datetime
import time
import json

def str2date(string,f="%Y-%m-%d"):#将字符串形式的时间转换成日期对象的函数
    year,month,day=time.strptime(string,f)[:3]
    date=datetime.date(year,month,day)
    return date

@admin.route('/',methods=['POST','GET'])
@login_required
def index():
    viewlog=Sitedata.query.filter_by(key='viewlog').first()
    date=json.loads(viewlog.value)

    xob=list(date.keys())#获取键名即字符串形式的日期组成的list;字典是无序的,这里开始的代码是用来将dict分为两个按时间排序的list。
    xlist=[]
    for x in xob:
        xlist.append(str2date(x))
    xlist.sort()#按时间从早到晚排序,date对象是可以比大小的,所以这里可以sort方法
    xob=[]
    for x in xlist:
        xob.append(str(x))
    yob=[]
    for x in xob:
        yob.append(date[x])

    return render_template('admin/index.html',xob=xob,yob=yob)

前端部分

这里我使用的是一个非常好用的开源可视化库ECharts,简单配置就可满足我们的使用了,我们来看看前端部分的代码吧!

templates/admin/index.html

<div id='viewschart' style='width:100%;height:400px;'></div>

<script>
    var myChart=echarts.init(document.getElementById('viewschart'));

    var option={
        title:{text:'浏览量统计'},
        tooltip:{},
        legend:{data:['浏览量']},
        xAxis:{
            data:{{ xob|safe }}
    },
        yAxis:{},
            series:[{
                name:'浏览量',
                type:'line',
                data:{{ yob|safe }}
                    }]
    };

    myChart.setOption(option);
</script>

接下来我们来测试一下吧

测试一下

开始测试之前我们需要做一件事,就是调整电脑的系统时间,这里的时间也可以代表网站服务器时间,往前调整,我这里是调整到2020年7月1日,差不多是十几天前,这么做是为了测试实际效果,然后修改完后创建完表并插入初始数据。

Flask使用ECharts实现网站浏览量统计图功能

运行flask项目,打开后台管理主页,因为还没有数据,所以图表为空。

Flask使用ECharts实现网站浏览量统计图功能

顺便访问几个页面,然后设置系统时间到7月2日,再次访问几个页面后切换到7月3日,如此往复几天,然后回去看看效果。

Flask使用ECharts实现网站浏览量统计图功能

接下来我们多循环几次上面的操作,看看超过十天会不会删除超过的部分。

Flask使用ECharts实现网站浏览量统计图功能

可以看到十天以前的部分会被删除,所以就不会显示在这里啦。

其他

以上就是一个简单的网站浏览统计折线图效果的实现啦,整个功能实际上只是在对三条数据进行操作:datelog,todayviews,viewlog,理解了first()函数中的代码后还是比较简单的;如果你有什么问题或者发现了一些错误欢迎在下面留言喔,我看到了会及时回复的。

评论

Felix 管理员

看了一下之前写的,有一点问题稍微改改会更好,就是不建议将统计的代码放到before_request那里,这样会把一些访问接口或者是爬虫给统计到,会影响数据准确性。可以把before_request里面的代码放到一个独立的路由下面,然后在需要统计的页面用js动态调用就好。

回复

Felix 管理员

回复 @hy: 抱歉,我这个在线上了,可能不方便给你源码,不过推荐你一本书《Flask web开发 基于python的web应用开发实战 第二版》,这本书可能会比较官方文档更适合入门。如果有问题的话,可以跟我说下,我看能帮上些什么忙。

回复

hy

你好,请问可以发我一份源码吗,最近正在研究flask的相关内容,万分感谢!

回复

  • 最新随笔

  • 对我来说,写东西很好的一点是,自己写的东西,后面基本都会再看,一遍或者很多遍。这样就可以很好地发现自己之前的漏洞,并看看现在有什么变化。
  • 前段时间应该是百度更新了页面结构,那个黑帽泛目录网站我也没有去更新代码,就导致了文章页没有相关推荐,没想到这时候谷歌开始收录了,而且效果还不错。其实之前在谷歌一直不收录的时候就有怀疑过没有尽头的相关推荐很可能是导致不收录的原因,不过也一直没有去管它,而这次误打误撞的收录也恰恰证实了这个想法。(btw,其实这个网站从出现固定链接开始叫泛目录应该不太准确)
  • 最近吉他谱网站iloveguitar上线了,这几天在填充一些内容花了一些时间,这个网站也是使用flask作为后端框架的,前端UI框架使用的是bootstrap4,喜欢吉他的小伙伴欢迎来这里逛一逛哟,有什么建议欢迎在留言板提一下哟。
  • 那个泛目录网站发布了几个星期了,百度没怎么来爬,收了一个首页,谷歌直接就不收了,倒是一个查外链的网站天天来爬(笑哭),还有建议一点就是泛目录还是用在有建站历史的域名比较好(因为已经过了审核期),新域名就尽量不要用了。
  • 刚刚又发了一篇,最近文章百度都不太收录,orz,哈哈
  • 之前那个用python做的静态资源服务器,我又发现了一大用处,现在我都用来在局域网里的不同电脑之间传输文件,真的好用哈哈
  • 今天交换到了博客有史以来第一条友情链接
  • 刚刚更新了一下博客,修复了一些bug,添加了一个项目页