之前我在翻找浏览器历史记录的时候发现,单独查询某一天的记录很方便,但是综合分析一段时间的浏览记录就比较困难。通过查询得知,Chrome浏览器的历史记录数据存储在名为History的sqlite数据库文件中。
所以我打算通过python编写一个能够分析一段时间内浏览记录的程序,并且通过图表的形式将分析结果展示在网页上,分析结果包括浏览时间、次数、搜索引擎偏好等等。
(1)程序能运行,功能丰富。(需求提交源代码,并建议录制程序运行的视频)
(2)综合实践报告,要体现实验分析、设计、实现过程、结果等信息,格式规范,逻辑清晰,结构合理。
(3)在实践报告中,需要对全课进行总结,并写课程感想体会、意见和建议等。
与解析历史记录文件数据有关的文件为history_data.py
文件。
# 连接sqlite数据库,执行查询语句,返回查询结构,最终关闭数据库连接。
def query_sqlite_db(history_db, query):
# 查询sqlite数据库
conn = sqlite3.connect(history_db)
cursor = conn.cursor()
select_statement = query
cursor.execute(select_statement)
# 获取数据,数据格式为元组(tuple)
results = cursor.fetchall()
cursor.close()
conn.close()
return results
# 设置数据库查询语句select_statement,调用query_sqlite_db()函数,获取解析后的历史记录文件数据。并对返回后的历史记录数据文件按照不同元素规则进行排序。
def get_history_data(history_file_path):
try:
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个元素进行排序
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‘
至此,成功获取经过排序的解析后的浏览记录数据文件。
与web服务器基本配置有关的文件为app_configuration.py
和app.py
文件。
本项目使用dash框架。
dash是一款基于python的web轻量级框架,无需js即可轻松运行。
适合用于比较简单的web页面的快速部署,如数据可视化,图表展示等。
import dash
# 配置一个dash服务器
app = dash.Dash(__name__)
#设置web服务器的端口号,访问权限,静态资源目录等
# 设置网页标题
app.title = ‘Browser History Analysis‘
# 开启加载本地css和js文件模式
app.css.config.serve_locally = True
app.scripts.config.serve_locally = True
app.layout = app_layout
# 回调,用于更新web页面数据
app_callback_function()
# 开始运行web服务器
if __name__ == ‘__main__‘:
# 是否是在本地运行(测试)
app_local = False
if(app_local):
app.run_server(host=‘127.0.0.1‘, debug=True, port=‘8090‘)
else:
app.run_server(host=‘0.0.0.0‘, debug=False, port=‘8090‘)
与前端部署有关的文件为app_layout.py
和app_plot.py
以及assets
目录。
在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%)‘}
),
],
)
]
)
在app_plot.py
中,使用plotly
库绘制图表。plotly
库是一个用于具有web交互功能的画图组件库。
# 绘制 页面访问频率排名 柱状图
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
目录下包含的数据为css
,用于前端布局。
与后台部署有关的文件为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
对象,并将这个对象返回到前端。至此,前端页面的布局就会显示出页面访问频率排名
的图表了。接下来,就是从Chrome历史记录文件中提取出想要的数据。由于Chrome历史记录文件是一个sqlite数据库,所以需要使用数据库语法提取出相关内容。
# 获取排序后的历史数据
def get_history_data(history_file_path):
try:
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个元素进行排序
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‘
每个字段代表的意思:
字段名 | 含义 |
---|---|
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的停留时间 |
运行成功后,通过浏览器打开http://localhost:8090:
上传浏览器History文件后,即可看到分析结果:
本次实验中我遇到了很多问题。例如由于Chrome浏览器在sqlite中存储的时间是以1601-01-01 00:00:00 为起始时间点的微妙计数,与Unix时间戳存在时间间隔,所以需要转换,这给我造成了很大困扰。
但通过查询相关资料,我最终解决了这些问题,这也是对我综合实践能力的一次提升。
这学期很幸运地选到了志强老师的Python课。我一开始以为在Python理论课上,应该就是老师照着PPT念(古板印象),同学们在底下各干各的。但是出乎意料的是,志强老师的理论课十分有趣,理论课也需要我们带着电脑跟着一起敲代码,不仅如此,志强老师的课上也会联系其他知识,例如知识点在实际项目中的应用、与其他编程语言的对比。其中令我印象深刻的是,志强老师在讲到序列的时候,用了王者荣耀英雄的案例,一下子感觉很熟悉(亲切)。
除此以外,课程的实验也设置得十分合理,有层次地由易到难、由浅到深,让我在每次实验过程中都能有全新的收获。正如志强老师在课程之初提到的“人生苦短,我用Python”,通过课程学习,我有了更深的体会。希望编程语言在我们日常生活中应该更多地被使用,这才是编程解决实际问题的魅力所在。
最后,再次感谢志强老师的教导,希望Python课程能越办越好!!!
20204311《Python程序设计》实验四 Python综合实践实验报告
原文:https://www.cnblogs.com/jam12138/p/14956750.html