20204311《Python程序设计》实验四 Python综合实践实验报告

简介

  • 实验名称:Python综合实践——浏览记录分析
  • 课程:《Python程序设计》
  • 班级: 2043
  • 姓名: 曲经民
  • 学号:20204311
  • 实验教师:王志强
  • 实验日期:2021年6月
  • 必修/选修: 选修课

实验内容

之前我在翻找浏览器历史记录的时候发现,单独查询某一天的记录很方便,但是综合分析一段时间的浏览记录就比较困难。通过查询得知,Chrome浏览器的历史记录数据存储在名为History的sqlite数据库文件中。
所以我打算通过python编写一个能够分析一段时间内浏览记录的程序,并且通过图表的形式将分析结果展示在网页上,分析结果包括浏览时间、次数、搜索引擎偏好等等。

实验要求

(1)程序能运行,功能丰富。(需求提交源代码,并建议录制程序运行的视频)
(2)综合实践报告,要体现实验分析、设计、实现过程、结果等信息,格式规范,逻辑清晰,结构合理。
(3)在实践报告中,需要对全课进行总结,并写课程感想体会、意见和建议等。

实验过程及结果

一. 实现应用的关键步骤设计

二. 代码编写及调试运行

1. 解析历史记录文件数据

与解析历史记录文件数据有关的文件为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'

至此,成功获取经过排序的解析后的浏览记录数据文件。

2. web服务器基本配置

与web服务器基本配置有关的文件为app_configuration.pyapp.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')

3. 前端页面部署

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

该函数的代码流程为:

  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目录下包含的数据为css,用于前端布局。

4. 后台部署

与后台部署有关的文件为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对象,并将这个对象返回到前端。至此,前端页面的布局就会显示出页面访问频率排名的图表了。

接下来,就是从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的停留时间

5.代码调式及运行


运行成功后,通过浏览器打开http://localhost:8090:

上传浏览器History文件后,即可看到分析结果:


三. 将代码上传码云

实验中遇到的问题

本次实验中我遇到了很多问题。例如由于Chrome浏览器在sqlite中存储的时间是以1601-01-01 00:00:00 为起始时间点的微妙计数,与Unix时间戳存在时间间隔,所以需要转换,这给我造成了很大困扰。
但通过查询相关资料,我最终解决了这些问题,这也是对我综合实践能力的一次提升。

思想感悟

    这学期很幸运地选到了志强老师的Python课。我一开始以为在Python理论课上,应该就是老师照着PPT念(古板印象),同学们在底下各干各的。但是出乎意料的是,志强老师的理论课十分有趣,理论课也需要我们带着电脑跟着一起敲代码,不仅如此,志强老师的课上也会联系其他知识,例如知识点在实际项目中的应用、与其他编程语言的对比。其中令我印象深刻的是,志强老师在讲到序列的时候,用了王者荣耀英雄的案例,一下子感觉很熟悉(亲切)。
    除此以外,课程的实验也设置得十分合理,有层次地由易到难、由浅到深,让我在每次实验过程中都能有全新的收获。正如志强老师在课程之初提到的“人生苦短,我用Python”,通过课程学习,我有了更深的体会。希望编程语言在我们日常生活中应该更多地被使用,这才是编程解决实际问题的魅力所在。
    最后,再次感谢志强老师的教导,希望Python课程能越办越好!!!

posted @ 2021-06-30 22:33  jam12138  阅读(641)  评论(1编辑  收藏  举报