转:用 Python 一键分析你的上网行为, 看是在认真工作还是摸鱼
简介
想看看你最近一年都在干嘛?看看你平时上网是在摸鱼还是认真工作?想写年度汇报总结,但是苦于没有数据?现在,它来了。
这是一个能让你了解自己的浏览历史的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
接下来,就是我们数据提取最核心的部分了,即从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
指的是查询数据库的规则,规则如下:
- 从(FROM)表
urls
中选择(SELECT)出以下字段urls.id
,urls.url
,urls.title
,urls.last_visit_time
,urls.visit_count
,依次代表URL的ID
,URL的地址
,URL的标题
,URL最后的访问时间
,URL的访问次数
。 - 接着,从(FROM)表
visits
中选择(SELECT)出以下字段visits.visit_time
,visits.from_visit
,visits.transition
,visits.visit_duration
,分别代表的是访问时间
,从哪个链接跳转过来的
,访问跳转
,访问停留的时间
。 - 对
步骤1
和步骤2
的结果进行连接,形成一个表格。然后从中(WHERE)筛选出符合urls.id = visits.url
的行。在urls
中,id
代表的是URL的id
,在visits
中,url
代表的也是URL的id
,所以只有当两者相等,才能连接一起,才能保留,否则就要去除这一行。 - 使用排序函数
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
。 - 返回一个排序好的数据
这里我们列出每个字段代表的意思:
字段名 | 含义 |
---|---|
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历史记录文件
- 首先,打开浏览器,输入
chrome://version/
,其中,个人资料路径
即为存放历史文件所在的目录。
- 跳转到
个人资料路径
,比如/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 一键分析你的上网行为, 看是在认真工作还是摸鱼的更多相关文章
- python一键电影搜索与下载
代码地址如下:http://www.demodashi.com/demo/14313.html python一键电影搜索与下载 概述 使用python搜索并爬取豆瓣电影信息,包括评分,主演,导演,类型 ...
- Python一键转Jar包,Java调用Python新姿势!
粉丝朋友们,不知道大家看故事看腻了没(要是没腻可一定留言告诉我^_^),今天这篇文章换换口味,正经的来写写技术文.言归正传,咱们开始吧! 本文结构: 需求背景 进击的Python Java和Pytho ...
- 方便代理下单的EcStore收货地址一键分析插件,同时支持淘宝/京东/一号店
使用EcStore开展分销的网站,代理需要经常代客下单,每个客户收货地址都不同,要选择和填写多个内容才能完成地址输入:省.市.区.详细地址.收货人姓名.手机电话等,非常麻烦,也容易输入错误.安装EcS ...
- Python股票分析系列——自动获取标普500股票列表.p5
该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第5部分.在本教程和接下来的几节中,我们将着手研究如何为更多公司提供大量的定价信息,以及如何一次 ...
- Python股票分析系列——基础股票数据操作(二).p4
该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第4部分.在本教程中,我们将基于Adj Close列创建烛台/ OHLC图,这将允许我介绍重新采 ...
- Python股票分析系列——基础股票数据操作(一).p3
该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第3部分.在本教程中,我们将使用我们的股票数据进一步分解一些基本的数据操作和可视化.我们将要使用 ...
- Python股票分析系列——数据整理和绘制.p2
该系列视频已经搬运至bilibili: 点击查看 欢迎来到Python for Finance教程系列的第2部分. 在本教程中,我们将利用我们的股票数据进一步分解一些基本的数据操作和可视化. 我们将要 ...
- Python股票分析系列——系列介绍和获取股票数据.p1
本系列转载自youtuber sentdex博主的教程视频内容 https://www.youtube.com/watch?v=19yyasfGLhk&index=4&list=PLQ ...
- Python数据采集分析告诉你为何上海二手房你都买不起
感谢关注Python爱好者社区公众号,在这里,我们会每天向您推送Python相关的文章实战干货. 来吧,一起Python. 对商业智能BI.大数据分析挖掘.机器学习,python,R等数据领域感兴趣的 ...
随机推荐
- 转载-Archunit的使用
Archunit的使用 注:开发的编辑器: Intellij Idea,JDK版本是JDK8 Archunit是什么,官网的英文介绍很好,建议阅读原文,"ArchUnit is a ...
- C++ 如何用百行代码实现线程安全的并发队列 | concurrent queue or blocking queue implemented in cpp
本文首发于个人博客https://kezunlin.me/post/cabccf5c/,欢迎阅读最新内容! concurrent queue or blocking queue implemented ...
- React 从入门到进阶之路(二)
在之前的文章中我们介绍了 React 开发的环境搭建及目录介绍和整理,本篇文章将介绍 React 创建组件.JSX 语法.绑定数据和绑定对象. 之前我们已经将项目运行了起来,我们再来看一下目录结构: ...
- Java入门系列之类继承、抽象类、接口(五)
前言 C#和Java关于类.抽象类.接口使用方式基本相似,只是对应关键字使用不同罢了,本节呢,我们只是对照C#和Java中关于这三个概念在具体使用时,看看有哪些不一样的地方. 类继承 C#和Java在 ...
- ubuntu上的安装.netcore2.1
.net core 在ubuntu上安装比较容易,依次执行正面语句即可 sudo apt-get install curl curl https://packages.microsoft.com/ke ...
- Xcode报错:could not attach to pid:"1764"
这种错误不是什么问题,按照参考链接操作即可,亲测有效: https://www.cnblogs.com/luorende/p/6295945.html 在运行项目时出现了如下错误 (基本上重新启动项目 ...
- JS While
JS While 只要指定条件为 true,循环就可以一直执行代码. while 循环 While 循环会在指定条件为真时循环执行代码块. 语法 while (条件) { 需要执行的代码 } whil ...
- 射频IC卡和IC卡读卡器的成本分析
当今射频IC卡和IC卡读卡器的种类繁多,很多人问IC卡读卡器多少钱,那么如何在满足我们需求的情况下最大的节省成本呢.下面就各种射频IC卡和IC卡读卡器来分析下各自的成本. ...
- none 和 host 网络的适用场景
我们会首先学习 Docker 提供的几种原生网络,以及如何创建自定义网络.然后探讨容器之间如何通信,以及容器与外界如何交互. Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 h ...
- 微信支付和微信支付通知基于sdk的说明(2)
前期准备工作 微信商户账户/密码(获取appid等信息) 微信公众号账户/密码(获取cert证书等信息,不做线上退款不需要证书) 下载php支付demo 从商户平台进入的话是以下界面或者直接搜索公众号 ...