一鍵分析你的上網(wǎng)行為, 看看你平時上網(wǎng)都在干嘛?

一鍵分析你的上網(wǎng)行為, 看看你平時上網(wǎng)都在干嘛?

簡介

想看看你最近一年都在干嘛?看看你平時上網(wǎng)是在摸魚還是認(rèn)真工作腥寇?想寫年度匯報總結(jié)侍匙,但是苦于沒有數(shù)據(jù)氮惯?現(xiàn)在,它來了想暗。

這是一個能讓你了解自己的瀏覽歷史的Chrome瀏覽歷史記錄分析程序妇汗,當(dāng)然了,他僅適用于Chrome瀏覽器或者以Chrome為內(nèi)核的瀏覽器说莫。

在該頁面中你將可以查看有關(guān)自己在過去的時間里所訪問瀏覽的域名杨箭、URL以及忙碌天數(shù)的前十排名以及相關(guān)的數(shù)據(jù)圖表。

<br />
<br />

部分截圖

demo.gif

<br />
<br />

代碼思路

1. 目錄結(jié)構(gòu)

首先储狭,我們先看一下整體目錄結(jié)構(gòu)

Code
├─ app_callback.py                          回調(diào)函數(shù)互婿,實現(xiàn)后臺功能
├─ app_configuration.py                     web服務(wù)器配置
├─ app_layout.py                            web前端頁面配置
├─ app_plot.py                              web圖表繪制
├─ app.py                                   web服務(wù)器的啟動
├─ assets                                   web所需的一些靜態(tài)資源文件
│  ├─ css                                   web前端元素布局文件
│  │  ├─ custum-styles_phyloapp.css
│  │  └─ stylesheet.css
│  ├─ image                                 web前端logo圖標(biāo)
│  │  ├─ GitHub-Mark-Light.png
│  └─ static                                web前端幫助頁面
│  │  ├─ help.html
│  │  └─ help.md
├─ history_data.py                          解析chrome歷史記錄文件
└─ requirement.txt                          程序所需依賴庫
  • app_callback.py
    該程序基于python,使用dash web輕量級框架進(jìn)行部署辽狈。app_callback.py主要用于回調(diào)慈参,可以理解為實現(xiàn)后臺功能。

  • app_configuration.py
    顧名思義刮萌,對web服務(wù)器的一些配置操作驮配。

  • app_layout..py
    web前端頁面配置,包含html, css元素着茸。

  • app_plot.py
    這個主要是為實現(xiàn)一些web前端的圖表數(shù)據(jù)壮锻。

  • app.py
    web服務(wù)器的啟動。

  • assets
    靜態(tài)資源目錄涮阔,用于存儲一些我們所需要的靜態(tài)資源數(shù)據(jù)猜绣。

  • history_data.py
    通過連接sqlite數(shù)據(jù)庫,并解析Chrome歷史記錄文件敬特。

  • requirement.txt
    運行本程序所需要的依賴庫掰邢。

<br />

2. 解析歷史記錄文件數(shù)據(jù)

與解析歷史記錄文件數(shù)據(jù)有關(guān)的文件為history_data.py文件。我們一一分析伟阔。

# 查詢數(shù)據(jù)庫內(nèi)容
def query_sqlite_db(history_db, query):

    # 查詢sqlite數(shù)據(jù)庫
    # 注意尸变,History是一個文件,沒有后綴名减俏。它不是一個目錄。
    conn = sqlite3.connect(history_db)
    cursor = conn.cursor()

    # 使用sqlite查看軟件碱工,可清晰看到表visits的字段url=表urls的字段id
    # 連接表urls和visits娃承,并獲取指定數(shù)據(jù)
    select_statement = query

    # 執(zhí)行數(shù)據(jù)庫查詢語句
    cursor.execute(select_statement)

    # 獲取數(shù)據(jù)奏夫,數(shù)據(jù)格式為元組(tuple)
    results = cursor.fetchall()

    # 關(guān)閉
    cursor.close()
    conn.close()

    return results

該函數(shù)的代碼流程為:

  1. 連接sqlite數(shù)據(jù)庫,執(zhí)行查詢語句历筝,返回查詢結(jié)構(gòu)酗昼,最終關(guān)閉數(shù)據(jù)庫連接。
# 獲取排序后的歷史數(shù)據(jù)
def get_history_data(history_file_path):

    try:

        # 獲取數(shù)據(jù)庫內(nèi)容
        # 數(shù)據(jù)格式為元組(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)

        # 將結(jié)果按第1個元素進(jìn)行排序
        # sort和sorted內(nèi)建函數(shù)會優(yōu)先排序第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]))

        # 返回排序后的數(shù)據(jù)
        return result_sort
    except:
        # print('讀取出錯!')
        return 'error'

該函數(shù)的代碼流程為:

  1. 設(shè)置數(shù)據(jù)庫查詢語句select_statement,調(diào)用query_sqlite_db()函數(shù)春弥,獲取解析后的歷史記錄文件數(shù)據(jù)呛哟。并對返回后的歷史記錄數(shù)據(jù)文件按照不同元素規(guī)則進(jìn)行排序。至此匿沛,經(jīng)過排序的解析后的歷史記錄數(shù)據(jù)文件獲取成功扫责。

<br />

3. web服務(wù)器基本配置

與web服務(wù)器基本配置有關(guān)的文件為app_configuration.pyapp.py文件。包括設(shè)置web服務(wù)器的端口號逃呼,訪問權(quán)限鳖孤,靜態(tài)資源目錄等。

<br />

4. 前端頁面部署

與前端部署有關(guān)的文件為app_layout.pyapp_plot.py以及assets目錄抡笼。

前端布局主要包括以下幾個元素:

  • 上傳歷史記錄文件組件
  • 繪制頁面訪問次數(shù)組件
  • 繪制頁面訪問停留總時間排名組件
  • 每日頁面訪問次數(shù)散點圖組件
  • 某日不同時刻訪問次數(shù)散點圖組件
  • 訪問次數(shù)最多的10個URL組件
  • 搜索關(guān)鍵詞排名組件
  • 搜索引擎使用情況組件

app_layout.py中苏揣,這些組件的配置大多一樣,和平常的html, css配置一樣推姻,所以我們僅僅以配置頁面訪問次數(shù)排名組件為例子平匈。

# 頁面訪問次數(shù)排名
html.Div(
    style={'margin-bottom':'150px'},
    children=[
        html.Div(
            style={'border-top-style':'solid','border-bottom-style':'solid'},
            className='row',
            children=[
                html.Span(
                    children='頁面訪問次數(shù)排名, ',
                    style={'font-weight': 'bold', 'color':'red'}
                ),

                html.Span(
                    children='顯示個數(shù):',
                ),
                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編寫的拾碌,但是只要具備前端經(jīng)驗的人吐葱,都可以輕而易舉地在此基礎(chǔ)上新增或者刪除一些元素,所以我們就不詳細(xì)講如何使用html和css了校翔。

app_plot.py中弟跑,主要是以繪制圖表相關(guān)的。使用的是plotly庫防症,這是一個用于具有web交互的畫圖組件庫孟辑。
這里以繪制頁面訪問頻率排名 柱狀圖為例子,講講如何使用plotly庫進(jìn)行繪制蔫敲。

# 繪制 頁面訪問頻率排名 柱狀圖
def plot_bar_website_count_rank(value, history_data):

    # 頻率字典
    dict_data = {}

    # 對歷史記錄文件進(jìn)行遍歷
    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個頻率最高的數(shù)據(jù)
    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='網(wǎng)站'),
            yaxis=dict(title='次數(shù)')
        )
    )


    return figure

該函數(shù)的代碼流程為:

  1. 首先饲嗽,對解析完數(shù)據(jù)庫文件后返回的history_data進(jìn)行遍歷,獲得url數(shù)據(jù)奈嘿,并調(diào)用url_simplification(url)對齊進(jìn)行簡化貌虾。接著,依次將簡化后的url存入字典中裙犹。
  2. 調(diào)用get_top_k_from_dict(dict_data, k)尽狠,從字典dict_data中獲取前k個最大值的數(shù)據(jù)衔憨。
  3. 接著,開始繪制柱狀圖了袄膏。使用go.Bar()繪制柱狀圖践图,其中,xy代表的是屬性和屬性對應(yīng)的數(shù)值沉馆,為list格式码党。xaxisyaxis`分別設(shè)置相應(yīng)坐標(biāo)軸的標(biāo)題
  4. 返回一個figure對象,以便于傳輸給前端斥黑。

assets目錄下包含的數(shù)據(jù)為imagecss揖盘,都是用于前端布局。

<br />

5. 后臺部署

與后臺部署有關(guān)的文件為app_callback.py文件心赶。這個文件使用回調(diào)的方式對前端頁面布局進(jìn)行更新扣讼。

首先,我們看看關(guān)于頁面訪問頻率排名的回調(diào)函數(shù):


# 頁面訪問頻率排名
@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:
        # 取消更新頁面數(shù)據(jù)
        raise dash.exceptions.PreventUpdate("cancel the callback")

該函數(shù)的代碼流程為:

  1. 首先確定好輸入是什么(觸發(fā)回調(diào)的數(shù)據(jù))缨叫,輸出是什么(回調(diào)輸出的數(shù)據(jù))椭符,需要帶上什么數(shù)據(jù)。dash.dependencies.Input指的是觸發(fā)回調(diào)的數(shù)據(jù)耻姥,而dash.dependencies.Input('input_website_count_rank', 'value')表示當(dāng)idinput_website_count_rank的組件的value發(fā)生改變時销钝,會觸發(fā)這個回調(diào)。而該回調(diào)經(jīng)過update(value, store_memory_history_data)的結(jié)果會輸出到idgraph_website_count_rankvalue琐簇,通俗來講蒸健,就是改變它的值。
  2. 對于def update(value, store_memory_history_data)的解析婉商。首先是判斷輸入數(shù)據(jù)store_memory_history_data是否不為空對象似忧,接著讀取歷史記錄文件history_data,接著調(diào)用剛才所說的app_plot.py文件中的plot_bar_website_count_rank()丈秩,返回一個figure對象盯捌,并將這個對象返回到前端。至此蘑秽,前端頁面的布局就會顯示出頁面訪問頻率排名的圖表了饺著。

還有一個需要說的就是關(guān)于上次文件的過程,這里我們先貼出代碼:

# 上傳文件回調(diào)
@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編碼的數(shù)據(jù)
        content_type, content_string = contents.split(',')

        # 將客戶端上傳的文件進(jìn)行base64解碼
        decoded = base64.b64decode(content_string)

        # 為客戶端上傳的文件添加后綴肠牲,防止文件重復(fù)覆蓋
        # 以下方式確保文件名不重復(fù)
        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)

        # 創(chuàng)建存放文件的目錄
        if (not (exists('data'))):
            makedirs('data')

        # 欲寫入的文件路徑
        path = 'data' + '/' + file_name

        # 寫入本地磁盤文件
        with open(file=path, mode='wb+') as f:
            f.write(decoded)


        # 使用sqlite讀取本地磁盤文件
        # 獲取歷史記錄數(shù)據(jù)
        history_data = get_history_data(path)
        
        # 獲取搜索關(guān)鍵詞數(shù)據(jù)
        search_word = get_search_word(path)

        # 判斷讀取到的數(shù)據(jù)是否正確
        if (history_data != 'error'):
            # 找到
            date_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            print('新接收到一條客戶端的數(shù)據(jù), 數(shù)據(jù)正確, 時間:{}'.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('新接收到一條客戶端的數(shù)據(jù), 數(shù)據(jù)錯誤, 時間:{}'.format(date_time))
            return  None

    return None

該函數(shù)的代碼流程為:

  1. 首先判斷用戶上傳的數(shù)據(jù)contents是否不為空幼衰,接著將客戶端上傳的文件進(jìn)行base64解碼。并且缀雳,為客戶端上傳的文件添加后綴渡嚣,防止文件重復(fù)覆蓋,最終將客戶端上傳的文件寫入本地磁盤文件。

  2. 寫入完畢后严拒,使用sqlite讀取本地磁盤文件扬绪,若讀取正確,則返回解析后的數(shù)據(jù)裤唠,否則返回None

<br />
<br />

如何運行

在線演示程序:http://39.106.118.77:8090(普通服務(wù)器,勿測壓)

運行本程序十分簡單莹痢,只需要按照以下命令即可運行:

# 跳轉(zhuǎn)到當(dāng)前目錄
cd 目錄名
# 先卸載依賴庫
pip uninstall -y -r requirement.txt
# 再重新安裝依賴庫
pip install -r requirement.txt
# 開始運行
python app.py

# 運行成功后种蘸,通過瀏覽器打開http://localhost:8090

<br />
<br />

接下來,就是我們數(shù)據(jù)提取最核心的部分了竞膳,即從Chrome歷史記錄文件中提取出我們想要的數(shù)據(jù)航瞭。由于Chrome歷史記錄文件是一個sqlite數(shù)據(jù)庫,所以我們需要使用數(shù)據(jù)庫語法提取出我們想要的內(nèi)容坦辟。

# 獲取排序后的歷史數(shù)據(jù)
def get_history_data(history_file_path):

    try:

        # 獲取數(shù)據(jù)庫內(nèi)容
        # 數(shù)據(jù)格式為元組(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)

        # 將結(jié)果按第1個元素進(jìn)行排序
        # sort和sorted內(nèi)建函數(shù)會優(yōu)先排序第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]))

        # 返回排序后的數(shù)據(jù)
        return result_sort
    except:
        # print('讀取出錯!')
        return 'error'

這里我們列出每個字段代表的意思:

字段名 含義
urls.id url的編號
urls.url url的地址
urls.title url的標(biāo)題
urls.last_visit_time url的最后訪問時間
urls.visit_count url的訪問次數(shù)
urls.visit_time url的訪問時間
urls.from_visit 從哪里訪問到這個url
urls.transition url的跳轉(zhuǎn)
urls.visit_duration url的停留時間

<br />
<br />

6. 如何獲取Chrome歷史記錄文件

  1. 首先锉走,打開瀏覽器滨彻,輸入chrome://version/,其中挪蹭,個人資料路徑即為存放歷史文件所在的目錄亭饵。
    image.png
  1. 跳轉(zhuǎn)到個人資料路徑,比如/Users/xxx/Library/Application Support/Google/Chrome/Default梁厉,找到一個叫History的文件辜羊,這個文件即為歷史記錄文件。
    image.png

<br />
<br />
<br />

補充

完整版源代碼存放在github上词顾,有需要的可以下載

項目持續(xù)更新八秃,歡迎您star本項目

<br />
<br />

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市肉盹,隨后出現(xiàn)的幾起案子昔驱,更是在濱河造成了極大的恐慌,老刑警劉巖垮媒,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舍悯,死亡現(xiàn)場離奇詭異,居然都是意外死亡睡雇,警方通過查閱死者的電腦和手機萌衬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來它抱,“玉大人秕豫,你說我怎么就攤上這事。” “怎么了混移?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵祠墅,是天一觀的道長。 經(jīng)常有香客問我歌径,道長毁嗦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任回铛,我火速辦了婚禮狗准,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茵肃。我一直安慰自己腔长,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布验残。 她就那樣靜靜地躺著捞附,像睡著了一般。 火紅的嫁衣襯著肌膚如雪您没。 梳的紋絲不亂的頭發(fā)上鸟召,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音紊婉,去河邊找鬼药版。 笑死,一個胖子當(dāng)著我的面吹牛喻犁,可吹牛的內(nèi)容都是我干的槽片。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼肢础,長吁一口氣:“原來是場噩夢啊……” “哼还栓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起传轰,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤剩盒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后慨蛙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辽聊,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年期贫,在試婚紗的時候發(fā)現(xiàn)自己被綠了跟匆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡通砍,死狀恐怖玛臂,靈堂內(nèi)的尸體忽然破棺而出烤蜕,到底是詐尸還是另有隱情,我是刑警寧澤迹冤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布讽营,位于F島的核電站,受9級特大地震影響泡徙,放射性物質(zhì)發(fā)生泄漏橱鹏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一堪藐、第九天 我趴在偏房一處隱蔽的房頂上張望蚀瘸。 院中可真熱鬧,春花似錦庶橱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奏瞬,卻和暖如春枫绅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背硼端。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工并淋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珍昨。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓县耽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親镣典。 傳聞我的和親對象是個殘疾皇子兔毙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,101評論 1 32
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML標(biāo)準(zhǔn)兄春。 注意:講述HT...
    kismetajun閱讀 27,485評論 1 45
  • 摘要: 之前用Selenium做UI自動化測試從初學(xué)到熟練碰到過很多問題澎剥,這里就不一一細(xì)說了,所以把最基本的操作都...
    Vicky_習(xí)慣做唯一閱讀 11,382評論 1 23
  • 夫人的夫
    蔣觀將閱讀 210評論 0 0
  • (文/亦濃) “怎么又空著手回來了赶舆?”一進(jìn)家門哑姚,老媽看看青溪空空如也的兩手問。 沒有喜歡的芜茵,青溪悶悶的說完就準(zhǔn)備回...
    開在夜里的花兒閱讀 527評論 6 5