简介

想看看你最近一年都在干嘛?看看你平时上网是在摸鱼还是认真工作?想写年度汇报总结,但是苦于没有数据?现在,它来了。

这是一个能让你了解自己的浏览历史的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

该函数的代码流程为:

  1. 连接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'

该函数的代码流程为:

  1. 设置数据库查询语句select_statement,调用query_sqlite_db()函数,获取解析后的历史记录文件数据。并对返回后的历史记录数据文件按照不同元素规则进行排序。至此,经过排序的解析后的历史记录数据文件获取成功。

3. web服务器基本配置

与web服务器基本配置有关的文件为app_configuration.pyapp.py文件。包括设置web服务器的端口号,访问权限,静态资源目录等。

4. 前端页面部署

与前端部署有关的文件为app_layout.pyapp_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

该函数的代码流程为:

  1. 首先,对解析完数据库文件后返回的history_data进行遍历,获得url数据,并调用url_simplification(url)对齐进行简化。接着,依次将简化后的url存入字典中。
  2. 调用get_top_k_from_dict(dict_data, k),从字典dict_data中获取前k个最大值的数据。
  3. 接着,开始绘制柱状图了。使用go.Bar()绘制柱状图,其中,xy代表的是属性和属性对应的数值,为list格式xaxisyaxis`分别设置相应坐标轴的标题
  4. 返回一个figure对象,以便于传输给前端。

assets目录下包含的数据为imagecss,都是用于前端布局。

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")

该函数的代码流程为:

  1. 首先确定好输入是什么(触发回调的数据),输出是什么(回调输出的数据),需要带上什么数据。dash.dependencies.Input指的是触发回调的数据,而dash.dependencies.Input('input_website_count_rank', 'value')表示当idinput_website_count_rank的组件的value发生改变时,会触发这个回调。而该回调经过update(value, store_memory_history_data)的结果会输出到idgraph_website_count_rankvalue,通俗来讲,就是改变它的值。
  2. 对于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

该函数的代码流程为:

  1. 首先判断用户上传的数据contents是否不为空,接着将客户端上传的文件进行base64解码。并且,为客户端上传的文件添加后缀,防止文件重复覆盖,最终将客户端上传的文件写入本地磁盘文件。

  2. 写入完毕后,使用sqlite读取本地磁盘文件,若读取正确,则返回解析后的数据,否则返回None

接下来,就是我们数据提取最核心的部分了,即从Chrome历史记录文件中提取出我们想要的数据。由于Chrome历史记录文件是一个sqlite数据库,所以我们需要使用数据库语法提取出我们想要的内容。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
# 获取排序后的历史数据
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指的是查询数据库的规则,规则如下:

  1. 从(FROM)表urls中选择(SELECT)出以下字段urls.id, urls.url, urls.title, urls.last_visit_time, urls.visit_count,依次代表URL的IDURL的地址URL的标题URL最后的访问时间URL的访问次数
  2. 接着,从(FROM)表visits中选择(SELECT)出以下字段visits.visit_time, visits.from_visit, visits.transition, visits.visit_duration,分别代表的是访问时间从哪个链接跳转过来的访问跳转访问停留的时间
  3. 步骤1步骤2的结果进行连接,形成一个表格。然后从中(WHERE)筛选出符合urls.id = visits.url的行。在urls中,id代表的是URL的id,在visits中,url代表的也是URL的id,所以只有当两者相等,才能连接一起,才能保留,否则就要去除这一行。
  4. 使用排序函数sorted,这个函数依次是以x[0]x[1]x[2]x[3]x[4]x[5]x[6]x[7]x[8]进行排序,也就是指的是urls.id, urls.url, urls.title, urls.last_visit_time, urls.visit_count, visits.visit_time, visits.from_visit, visits.transition, visits.visit_duration
  5. 返回一个排序好的数据

这里我们列出每个字段代表的意思:

字段名 含义
urls.id url的编号
urls.url url的地址
urls.title url的标题
urls.last_visit_time url的最后访问时间
urls.visit_count url的访问次数
urls.visit_time url的访问时间
urls.from_visit 从哪里访问到这个url
urls.transition url的跳转
urls.visit_duration url的停留时间

6. 如何获取Chrome历史记录文件

  1. 首先,打开浏览器,输入chrome://version/,其中,个人资料路径即为存放历史文件所在的目录。

  2. 跳转到个人资料路径,比如/Users/xxx/Library/Application Support/Google/Chrome/Default,找到一个叫History的文件,这个文件即为历史记录文件。

如何运行

在线演示程序:http://39.106.118.77:8090(普通服务器,勿测压)

运行本程序十分简单,只需要按照以下命令即可运行:

# 跳转到当前目录
cd 目录名
# 先卸载依赖库
pip uninstall -y -r requirement.txt
# 再重新安装依赖库
pip install -r requirement.txt
# 开始运行
python app.py # 运行成功后,通过浏览器打开http://localhost:8090

转:用 Python 一键分析你的上网行为, 看是在认真工作还是摸鱼的更多相关文章

  1. python一键电影搜索与下载

    代码地址如下:http://www.demodashi.com/demo/14313.html python一键电影搜索与下载 概述 使用python搜索并爬取豆瓣电影信息,包括评分,主演,导演,类型 ...

  2. Python一键转Jar包,Java调用Python新姿势!

    粉丝朋友们,不知道大家看故事看腻了没(要是没腻可一定留言告诉我^_^),今天这篇文章换换口味,正经的来写写技术文.言归正传,咱们开始吧! 本文结构: 需求背景 进击的Python Java和Pytho ...

  3. 方便代理下单的EcStore收货地址一键分析插件,同时支持淘宝/京东/一号店

    使用EcStore开展分销的网站,代理需要经常代客下单,每个客户收货地址都不同,要选择和填写多个内容才能完成地址输入:省.市.区.详细地址.收货人姓名.手机电话等,非常麻烦,也容易输入错误.安装EcS ...

  4. Python股票分析系列——自动获取标普500股票列表.p5

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第5部分.在本教程和接下来的几节中,我们将着手研究如何为更多公司提供大量的定价信息,以及如何一次 ...

  5. Python股票分析系列——基础股票数据操作(二).p4

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第4部分.在本教程中,我们将基于Adj Close列创建烛台/ OHLC图,这将允许我介绍重新采 ...

  6. Python股票分析系列——基础股票数据操作(一).p3

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第3部分.在本教程中,我们将使用我们的股票数据进一步分解一些基本的数据操作和可视化.我们将要使用 ...

  7. Python股票分析系列——数据整理和绘制.p2

    该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第2部分. 在本教程中,我们将利用我们的股票数据进一步分解一些基本的数据操作和可视化. 我们将要 ...

  8. Python股票分析系列——系列介绍和获取股票数据.p1

    本系列转载自youtuber sentdex博主的教程视频内容 https://www.youtube.com/watch?v=19yyasfGLhk&index=4&list=PLQ ...

  9. Python数据采集分析告诉你为何上海二手房你都买不起

    感谢关注Python爱好者社区公众号,在这里,我们会每天向您推送Python相关的文章实战干货. 来吧,一起Python. 对商业智能BI.大数据分析挖掘.机器学习,python,R等数据领域感兴趣的 ...

随机推荐

  1. 基于node.js人脸识别之人脸对比

    基于node.js人脸识别之人脸对比 Node.js简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻塞式 I/O ...

  2. 单个div元素实现双边框

    昨天被问到一个很有意思的问题,单个div元素怎么实现双边框,当时脑子懵了一下,然后就回答出来用伪元素,别的实在是想不起来了,所以在此总结一下子防止以后再被问到 总结了一下大约有以下几种方案: 伪元素实 ...

  3. 多线程十 Timer

    定时/计算在java中主要使用的是Timer对象,他的内部依然是采用多线程方式进行处理 它有四个构造方法 方法名 作用 Timer() 空参 Timer(String name) 指定名字 Timer ...

  4. PAT 1012 The Best Rank 排序

    To evaluate the performance of our first year CS majored students, we consider their grades of three ...

  5. oracle将时间加一天,加小时,加分,加秒

    前言 oracle 时间类型可以直接相加,但加的是天,以天为单位,我们了解了这个,加一天,一小时,一分,一秒就都简单了. 加一天 select to_date('2019-08-15 22:03:10 ...

  6. (三十七)c#Winform自定义控件-有标题的面板-HZHControls

    官网 http://www.hzhcontrols.com 前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kww ...

  7. C# 使用GDI绘制雷达图

    最近项目要用C#实现画一个雷达图,搜了搜网上竟然找不到C#画雷达图的解决方案,那么自己实现一个吧 实现效果如下图: 代码如下: public static class RadarDemo { ; ; ...

  8. 松软科技web课堂:SQLServer之SUM() 函数

    SUM() 函数 SUM 函数返回数值列的总数(总额). SQL SUM() 语法 SELECT SUM(column_name) FROM table_name SQL SUM() 实例 我们拥有下 ...

  9. Java - IO 内存流和打印流

    IO 内存流和打印流操作 字符编码 计算机中所有的信息组成都是二进制数据,所有能够描述的中文文字都是经过处理后的结果:所有的语言文字都会使用编码来进行描述,例如:ASCII码 常见编码 GBK/GB2 ...

  10. Linux—vi/vim命令详解

    如何在 vi 里搜索关键字 在命令模式下敲斜杆( / )这时在状态栏(也就是屏幕左下脚)就出现了 "/" 然后输入你要查找的关键字敲回车就行了. 如果你要继续查找此关键字,敲字符 ...