简介
想看看你最近一年都在干嘛?看看你平时上网是在摸鱼还是认真工作?想写年度汇报总结,但是苦于没有数据?现在,它来了。
这是一个能让你了解自己的浏览历史的Chrome浏览历史记录分析程序,当然了,他仅适用于Chrome浏览器或者以Chrome为内核的浏览器。
在该页面中你将可以查看有关自己在过去的时间里所访问浏览的域名、URL以及忙碌天数的前十排名以及相关的数据图表。
部分截图
代码思路
1. 目录结构
首先,我们先看一下整体目录结构
Code ├─ app_callback.py 回调函数,实现后台功能 ├─ app_configuration.py web服务器配置 ├─ app_layout.py web前端页面配置 ├─ app_plot.py web图表绘制 ├─ app.py web服务器的启动 ├─ assets web所需的一些静态资源文件 │ ├─ css web前端元素布局文件 │ │ ├─ custum-styles_phyloapp.css │ │ └─ stylesheet.css │ ├─ image web前端logo图标 │ │ ├─ GitHub-Mark-Light.png │ └─ static web前端帮助页面 │ │ ├─ help.html │ │ └─ help.md ├─ history_data.py 解析chrome历史记录文件 └─ requirement.txt 程序所需依赖库
- app_callback.py
该程序基于python,使用dash web轻量级框架进行部署。app_callback.py主要用于回调,可以理解为实现后台功能。
- app_configuration.py
顾名思义,对web服务器的一些配置操作。
- app_layout..py
web前端页面配置,包含html, css元素。
- app_plot.py
这个主要是为实现一些web前端的图表数据。
- app.py
web服务器的启动。
- assets
静态资源目录,用于存储一些我们所需要的静态资源数据。
- history_data.py
通过连接sqlite数据库,并解析Chrome历史记录文件。
- requirement.txt
运行本程序所需要的依赖库。
2. 解析历史记录文件数据
与解析历史记录文件数据有关的文件为history_data.py文件。我们一一分析。
# 查询数据库内容 def query_sqlite_db(history_db, query): # 查询sqlite数据库 # 注意,History是一个文件,没有后缀名。它不是一个目录。 conn = sqlite3.connect(history_db) cursor = conn.cursor() # 使用sqlite查看软件,可清晰看到表visits的字段url=表urls的字段id # 连接表urls和visits,并获取指定数据 select_statement = query # 执行数据库查询语句 cursor.execute(select_statement) # 获取数据,数据格式为元组(tuple) results = cursor.fetchall() # 关闭 cursor.close() conn.close() return results
该函数的代码流程为:
连接sqlite数据库,执行查询语句,返回查询结构,最终关闭数据库连接。
# 获取排序后的历史数据 def get_history_data(history_file_path): try: # 获取数据库内容 # 数据格式为元组(tuple) select_statement = "SELECT urls.id, urls.url, urls.title, urls.last_visit_time, urls.visit_count, visits.visit_time, visits.from_visit, visits.transition, visits.visit_duration FROM urls, visits WHERE urls.id = visits.url;" result = query_sqlite_db(history_file_path, select_statement) # 将结果按第1个元素进行排序 # sort和sorted内建函数会优先排序第1个元素,然后再排序第2个元素,依此类推 result_sort = sorted(result, key=lambda x: (x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], x[8])) # 返回排序后的数据 return result_sort except: # print('读取出错!') return 'error'
该函数的代码流程为:
设置数据库查询语句select_statement,调用query_sqlite_db()函数,获取解析后的历史记录文件数据。并对返回后的历史记录数据文件按照不同元素规则进行排序。至此,经过排序的解析后的历史记录数据文件获取成功。
3. web服务器基本配置
与web服务器基本配置有关的文件为app_configuration.py和app.py文件。包括设置web服务器的端口号,访问权限,静态资源目录等。
4. 前端页面部署
与前端部署有关的文件为app_layout.py和app_plot.py以及assets目录。
前端布局主要包括以下几个元素:
- 上传历史记录文件组件
- 绘制页面访问次数组件
- 绘制页面访问停留总时间排名组件
- 每日页面访问次数散点图组件
- 某日不同时刻访问次数散点图组件
- 访问次数最多的10个URL组件
- 搜索关键词排名组件
- 搜索引擎使用情况组件
在app_layout.py中,这些组件的配置大多一样,和平常的html, css配置一样,所以我们仅仅以配置页面访问次数排名组件为例子。
# 页面访问次数排名 html.Div( style={'margin-bottom':'150px'}, children=[ html.Div( style={'border-top-style':'solid','border-bottom-style':'solid'}, className='row', children=[ html.Span( children='页面访问次数排名, ', style={'font-weight': 'bold', 'color':'red'} ), html.Span( children='显示个数:', ), dcc.Input( id='input_website_count_rank', type='text', value=10, style={'margin-top':'10px', 'margin-bottom':'10px'} ), ] ), html.Div( style={'position': 'relative', 'margin': '0 auto', 'width': '100%', 'padding-bottom': '50%', }, children=[ dcc.Loading( children=[ dcc.Graph( id='graph_website_count_rank', style={'position': 'absolute', 'width': '100%', 'height': '100%', 'top': '0', 'left': '0', 'bottom': '0', 'right': '0'}, config={'displayModeBar': False}, ), ], type='dot', style={'position': 'absolute', 'top': '50%', 'left': '50%', 'transform': 'translate(-50%,-50%)'} ), ], ) ] )
可以看到,虽然是python编写的,但是只要具备前端经验的人,都可以轻而易举地在此基础上新增或者删除一些元素,所以我们就不详细讲如何使用html和css了。
在app_plot.py中,主要是以绘制图表相关的。使用的是plotly库,这是一个用于具有web交互的画图组件库。
这里以绘制页面访问频率排名 柱状图为例子,讲讲如何使用plotly库进行绘制。
# 绘制 页面访问频率排名 柱状图 def plot_bar_website_count_rank(value, history_data): # 频率字典 dict_data = {} # 对历史记录文件进行遍历 for data in history_data: url = data[1] # 简化url key = url_simplification(url) if (key in dict_data.keys()): dict_data[key] += 1 else: dict_data[key] = 0 # 筛选出前k个频率最高的数据 k = convert_to_number(value) top_10_dict = get_top_k_from_dict(dict_data, k) figure = go.Figure( data=[ go.Bar( x=[i for i in top_10_dict.keys()], y=[i for i in top_10_dict.values()], name='bar', marker=go.bar.Marker( color='rgb(55, 83, 109)' ) ) ], layout=go.Layout( showlegend=False, margin=go.layout.Margin(l=40, r=0, t=40, b=30), paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', xaxis=dict(title='网站'), yaxis=dict(title='次数') ) ) return figure
该函数的代码流程为:
- 首先,对解析完数据库文件后返回的history_data进行遍历,获得url数据,并调用url_simplification(url)对齐进行简化。接着,依次将简化后的url存入字典中。
- 调用get_top_k_from_dict(dict_data, k),从字典dict_data中获取前k个最大值的数据。
- 接着,开始绘制柱状图了。使用go.Bar()绘制柱状图,其中,x和y代表的是属性和属性对应的数值,为list格式。xaxis和yaxis`分别设置相应坐标轴的标题
- 返回一个figure对象,以便于传输给前端。
- 而assets目录下包含的数据为image和css,都是用于前端布局。
5. 后台部署
与后台部署有关的文件为app_callback.py文件。这个文件使用回调的方式对前端页面布局进行更新。
首先,我们看看关于页面访问频率排名的回调函数:
# 页面访问频率排名 @app.callback( dash.dependencies.Output('graph_website_count_rank', 'figure'), [ dash.dependencies.Input('input_website_count_rank', 'value'), dash.dependencies.Input('store_memory_history_data', 'data') ] ) def update(value, store_memory_history_data): # 正确获取到历史记录文件 if store_memory_history_data: history_data = store_memory_history_data['history_data'] figure = plot_bar_website_count_rank(value, history_data) return figure else: # 取消更新页面数据 raise dash.exceptions.PreventUpdate("cancel the callback")
该函数的代码流程为:
首先确定好输入是什么(触发回调的数据),输出是什么(回调输出的数据),需要带上什么数据。dash.dependencies.Input指的是触发回调的数据,而dash.dependencies.Input('input_website_count_rank', 'value')表示当id为input_website_count_rank的组件的value发生改变时,会触发这个回调。而该回调经过update(value, store_memory_history_data)的结果会输出到id为graph_website_count_rank的value,通俗来讲,就是改变它的值。
对于def update(value, store_memory_history_data)的解析。首先是判断输入数据store_memory_history_data是否不为空对象,接着读取历史记录文件history_data,接着调用刚才所说的app_plot.py文件中的plot_bar_website_count_rank(),返回一个figure对象,并将这个对象返回到前端。至此,前端页面的布局就会显示出页面访问频率排名的图表了。
还有一个需要说的就是关于上次文件的过程,这里我们先贴出代码:
# 上传文件回调 @app.callback( dash.dependencies.Output('store_memory_history_data', 'data'), [ dash.dependencies.Input('dcc_upload_file', 'contents') ] ) def update(contents): if contents is not None: # 接收base64编码的数据 content_type, content_string = contents.split(',') # 将客户端上传的文件进行base64解码 decoded = base64.b64decode(content_string) # 为客户端上传的文件添加后缀,防止文件重复覆盖 # 以下方式确保文件名不重复 suffix = [str(random.randint(0,100)) for i in range(10)] suffix = "".join(suffix) suffix = suffix + str(int(time.time())) # 最终的文件名 file_name = 'History_' + suffix # print(file_name) # 创建存放文件的目录 if (not (exists('data'))): makedirs('data') # 欲写入的文件路径 path = 'data' + '/' + file_name # 写入本地磁盘文件 with open(file=path, mode='wb+') as f: f.write(decoded) # 使用sqlite读取本地磁盘文件 # 获取历史记录数据 history_data = get_history_data(path) # 获取搜索关键词数据 search_word = get_search_word(path) # 判断读取到的数据是否正确 if (history_data != 'error'): # 找到 date_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) print('新接收到一条客户端的数据, 数据正确, 时间:{}'.format(date_time)) store_data = {'history_data': history_data, 'search_word': search_word} return store_data else: # 没找到 date_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) print('新接收到一条客户端的数据, 数据错误, 时间:{}'.format(date_time)) return None return None
该函数的代码流程为:
首先判断用户上传的数据contents是否不为空,接着将客户端上传的文件进行base64解码。并且,为客户端上传的文件添加后缀,防止文件重复覆盖,最终将客户端上传的文件写入本地磁盘文件。
写入完毕后,使用sqlite读取本地磁盘文件,若读取正确,则返回解析后的数据,否则返回None
如何运行
在线演示程序:http://39.106.118.77:8090(普通服务器,勿测压)
运行本程序十分简单,只需要按照以下命令即可运行:
# 跳转到当前目录 cd 目录名 # 先卸载依赖库 pip uninstall -y -r requirement.txt # 再重新安装依赖库 pip install -r requirement.txt # 开始运行 python app.py # 运行成功后,通过浏览器打开http://localhost:8090
补充
完整版源代码存放在github上,有需要的可以下载
项目持续更新,欢迎您star本项目
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]