爬蟲實戰(三):微博使用者資訊分析

ACool發表於2018-07-15

前敘

系列文章:

爬蟲實戰(一):爬取微博使用者資訊

爬蟲實戰(二):Selenium 模擬登入並爬取資訊

爬蟲實戰(三):微博使用者資訊分析

該系列文章介紹了什麼?

1.爬蟲分析和處理方法

2.Python中的資料庫操作方法

3.Selenium瀏覽器自動化以及無頭瀏覽器使用方法

4.對資料進行詞雲分析的方法

5.對資料進行視覺化的方法

6.LDA隱含狄利克雷分佈模型的建模和使用方法

前言

前面 這篇文章 介紹了爬取微博使用者資料和動態並將資料儲存在資料庫的過程。下面來說一下對資料庫資料與分析的過程。

資料庫分析

首先先來回顧一下前一篇文章中談到的資料庫欄位。下圖是 weibo 資料庫中的三張表:

oXauR.png

其中每個欄位對應的含義請見原始碼

再來看一下資料庫中每張表裡面的內容,首先是使用者資料表中的內容:

oXLLy.png

可以看到和微博資料頁的資料是對應的。

再來看看使用者動態資料表:

oXEGi.png

裡面包含了使用者動態內容和動態釋出時間,所以我們首先想到可以對微博動態內容進行處理。

視覺化分析

瞭解了資料庫結構以後,我們可以對微博動態和動態釋出時間進行視覺化處理,在這裡,筆者做了以下幾點分析:

  • 微博動態釋出時間處理:畫出微博釋出時間統計圖
  • 微博動態簡單視覺化:詞雲
  • 微博動態詞頻處理:詞頻統計以及生成詞頻圖

以上三種分析程式均位於 Data_analysis.py 中, 其 main 函式程式碼如下:

#可以指定需要分析的使用者的uid(必須先存在conf.yaml裡面,並且執行了一次爬蟲程式)
def main(uid):
    time_lists,str=get_time_str(uid)#將資料庫中的微博動態轉化為字串
    plot_create_time(time_lists)#畫出微博釋出時間統計圖
    with open('data/stop_words.txt') as f:
        stop_words = f.read().split('\n')
    str=format_content(str)#去掉表情和一些不必要的符號
    word_list=word_segmentation(str,stop_words)#分詞並去除停用詞
    create_wordcloud(word_list) #畫出詞雲
    counter = word_frequency(word_list, 10)# 返回前 top_N 個值,如果不指定則返回所有值
    print(counter)
    plot_chart(counter)#會生成詞頻圖儲存在weibo_wordfrq.html中
複製程式碼

那麼這三種分析具體如何實現呢?接下來筆者將對其進行一一闡述。

微博釋出時間統計

要對微博動態內容進行視覺化分析,第一步是要將微博動態內容整合到一起,下面這段程式碼是將使用者微博中的所有動態拼接到一起,也就是 main 函式裡面的第一行程式碼所呼叫的函式。

# time_lists,str=get_time_str(uid)# main 函式第一行程式碼
# 將資料庫中的微博動態轉化為字串
def get_time_str(uid):
    _, engine = Connect('../conf.yaml')  # 連線資料庫
    conn = engine.connect()
    metadata = MetaData(engine)
    wb_data = Table('wb_data',metadata, autoload=True)
    s = select([wb_data]).where(wb_data.c.uid == uid) #從wb_data表中選擇uid對應的資料
    res = conn.execute(s)
    conn.close()
    str = ''
    time_lists = []
    for row in res:
        str += row[2] + '\n'#拼接動態欄位
        time_lists.append(row[3]) #將時間轉化為列表物件,方便後續處理
    return time_lists, str
複製程式碼

返回的 time_lists 為動態釋出時間列表, str 為所有動態拼接到一起的最終字串.在這裡因為是對時間進行統計,所以我們只需要關注 time_lists 這個列表.先來看看這個列表裡面存放的具體內容:

[....,'02月02日 29', '01月21日 30', '01月20日 31', '01月10日 32', '01月02日 33', '01月01日 34', '2017-11-30', '2017-11-29', '2017-11-04', '2017-10-29', '2017-10-27',...]
複製程式碼

這是我特意取的一段,剛好兩種格式的時間都包含在內。這個列表裡面包含的時間是從最近的月份和日期到最初的年份-月份-日期.例如上面的 '2017-11-30' 前面的日期格式均是 XX月XX日XX ,而 '01月01日 34' 後面的同理均是 20XX-XX-XX ,排序方式是由近到遠 。在這裡,我考慮分析每天釋出的微博動態數和時間的變化趨勢,並且將時間排列為離現在越遠離原點越近。

於是便有了下面這段程式碼:

# plot_create_time(time_lists)# main 函式第二行程式碼
#畫出微博釋出時間的統計圖
def plot_create_time(time_lists):
    recent_time = re.compile(r'\d{2}月\d{2}日',re.S)
    long_time = re.compile(r'(\d{4}-\d{2}-\d{2})',re.S)
    tmp_lists = []#儲存**月**日格式的資料
    tmp_nums = []#統計**月**日發帖數量
    long_lists = []#儲存20**-**-**格式的資料
    long_nums = []#統計20**-**-**發帖數量
    for t in time_lists:
        res = re.findall(recent_time, t)
        if(res):#res[0]為**月**日格式的資料
            if(not tmp_lists or res[0]!= tmp_lists[-1]):#列表為空或者不與前一個日期重複
                tmp_lists.append(res[0])
                tmp_nums.append(1)
            else:#與前一個日期重複,計數加一
                tmp_nums[-1]+=1
        else:#res[0]20**-**-**格式的資料
            res = re.findall(long_time,t)

            if(not long_lists or res[0]!=long_lists[-1]):
                long_lists.append(res[0])
                long_nums.append(1)
            else:
                long_nums[-1]+=1
    #將時間按照從遠到進的順序排列
    tmp_lists.reverse()
    tmp_nums.reverse()
    long_lists.reverse()
    long_nums.reverse()
    time_list = long_lists + tmp_lists
    time_nums = long_nums + tmp_nums
    
    #呼叫 pyecharts 包渲染出統計圖
    chart = Bar('使用者微博動態釋出時間')
    chart.add('動態數', time_list, time_nums, is_more_utils=True,datazoom_range=[10,40],is_datazoom_show=True)
    chart.render("weibo_dynamic.html")
複製程式碼

於是微博釋出時間統計圖便畫出來了,是一個動態的圖,效果如下:

weibo_dynamic.gif

詞雲分析

要對動態內容進行分析,首先要進行分詞處理,並且,我們知道常用詞語中有很多語氣詞等,會影響關鍵詞語的權重,所以在進行分析之前需要去掉這些詞語,這種詞語在資訊檢索叫做 停用詞 。此外,動態中含有的換行符,html程式碼等不必要的符號也需要去掉。

所以 main 函式中在得到詞雲之前有下面四行程式碼:

with open('data/stop_words.txt') as f:#從儲存有停用詞的檔案中讀取停用詞
        stop_words = f.read().split('\n')
str=format_content(str)# 去掉表情和一些不必要的符號
word_list=word_segmentation(str,stop_words)#分詞並去除停用詞
複製程式碼

其中呼叫了 word_segmentation 函式,該函式的內容如下:

# 分詞並去除停用詞
def word_segmentation(content, stop_words):
    # 使用 jieba 分詞對文字進行分詞處理
    jieba.enable_parallel()
    seg_list = jieba.cut(content)
    seg_list = list(seg_list)

    # 去除停用詞
    user_dict = [' ', '噠']
    filter_space = lambda w: w not in stop_words and w not in user_dict
    word_list = list(filter(filter_space, seg_list))

    return word_list
複製程式碼

在這裡,我使用 jieba 進行分詞,再對分詞結果進行過濾,去除停用詞,並返回該分詞結果列表。然後使用 word_list 中的資料來畫出詞雲,具體程式碼如下:

#create_wordcloud(word_list) # main 函式第7行程式碼
#畫出詞雲
def create_wordcloud(content,image='weibo.jpg',max_words=5000,max_font_size=50):

    cut_text = " ".join(content)
    cloud = WordCloud(
        # 設定字型,不指定就會出現亂碼
        font_path="HYQiHei-25J.ttf",
        # 允許最大詞彙
        max_words=max_words,
        # 設定背景色
        # background_color='white',
        # 最大號字型
        max_font_size=max_font_size
    )
    word_cloud = cloud.generate(cut_text)
    word_cloud.to_file(image)
複製程式碼

最後得到結果 weibo.jpg ,開啟看一看:

weibo.jpg

這就是詞雲分析的結果,讀者可以根據自己需要,比如如果覺得原文和轉發出現頻率高價值不高可以加入停用詞中進行過濾。

詞頻處理

對微博動態中的詞語出現的頻率進行統計處理,首先需要詞語列表,因為在詞雲分析中我們已經得到了過濾掉停用詞的詞語列表 word_list ,所以在這裡可以直接拿來使用:

#counter = word_frequency(word_list, 10)# main 函式倒數第三行程式碼
# 詞頻統計
# 返回前 top_N 個值,如果不指定則返回所有值
# from collections import Counter
def word_frequency(word_list, *top_N):
    if top_N:
        counter = Counter(word_list).most_common(top_N[0])
    else:
        counter = Counter(word_list).most_common()

    return counter
複製程式碼

得到的 counter 內容為詞語以及出現的次陣列成的列表,例如:

[('感覺', 11), ('程式碼', 10), ('說', 9),('晚上', 9), ('終於', 7), ('麻蛋', 6), ('寫', 6), ('資料', 5), ('學校', 5), ('朋友', 4)]
複製程式碼

然後根據得到的詞頻畫圖並渲染:

# plot_chart(counter)# main 函式最後一行,會生成詞頻圖儲存在weibo_wordfrq.html中
#畫出詞頻圖,預設為柱狀圖
def plot_chart(counter, chart_type='Bar'):
    items = [item[0] for item in counter]
    values = [item[1] for item in counter]

    if chart_type == 'Bar':
        chart = Bar('微博動態詞頻統計')
        chart.add('詞頻', items, values, is_more_utils=True)
    else:
        chart = Pie('微博動態詞頻統計')
        chart.add('詞頻', items, values, is_label_show=True, is_more_utils=True)

    chart.render('weibo_wordfrq.html')
複製程式碼

最後得到排名前10位的詞語頻率統計圖:

weibowordfrq.gif

LDA 分析

我們忙活了很久,對使用者動態進行了多種分析,但是使用者的偏好和個性感覺還是沒有把握得很清楚,那麼這時候就需要使用一種方法從海量資訊中抽取關鍵部分,最好是能提取出主題,受之前這篇論文[1] 的啟發,筆者得知 LDA 便是一種能夠滿足上述條件的很好的分析方法。

隱含狄利克雷分佈(英語:Latent Dirichlet allocation,簡稱LDA)是一種主題模型,它可以將文件集中每篇文件的主題按照概率分佈的形式給出。同時它是一種無監督學習演算法,在訓練時不需要手工標註的訓練集,需要的僅僅是文件集以及指定主題的數量k即可。此外LDA的另一個優點則是,對於每一個主題均可找出一些詞語來描述它。

以上說明摘錄自維基百科.

本文著眼點在於實踐,所以只介紹在 python 中使用 LDA 進行分析的方法,並不闡述 LDA 的原理。更何況,在電腦科學和資料科學的學術講座中,講者在介紹到LDA時,都往往會把原理這部分直接跳過去。

該部分程式碼位於 LDA_Analysis.py ,首先可以看看 main 函式的內容:

def main(uid):
    wordlists, uid = getwords(uid)#獲取分詞列表
    lda, tf, tf_feature_names, tf_vectorizer = word2vec(wordlists)#將單詞轉化為向量
    Save_Topic_Words(lda, tf_feature_names, uid)#儲存主題到資料庫
    pyLDAvisUI(lda, tf, tf_vectorizer)#根據主題結果進行渲染
複製程式碼

第一步便是像之前那樣進行分詞並過濾停用詞,呼叫了 getwords 函式,程式碼如下:

#獲取微博動態資料
def getwords(uid):
    _,str = get_time_str(uid)  # 將資料庫中的微博動態轉化為字串,可以指定uid(conf.yaml裡面的)
    with open('data/stop_words.txt') as f:
        stop_words = f.read().split('\n')
    str = format_content(str)
    word_list = word_segmentation(str, stop_words)  # 分詞並去除停用詞
    return word_list,uid
複製程式碼

該函式內容和之前介紹的 Data_analysis.py 中的效果一樣,故在此不累述。

然後使用 sklearn 機器學習包中自帶的 LDA 處理方法進行處理,程式碼如下:

#lda, tf, tf_feature_names, tf_vectorizer = word2vec(wordlists)# main 函式第二行程式碼
#使用LDA進行微博動態主題建模與分析
def word2vec(word_list,n_features=1000,topics = 5):
    tf_vectorizer = CountVectorizer(strip_accents='unicode',
                                    max_features=n_features,
                                    #stop_words='english',已經進行過停用詞處理故不用重複處理
                                    max_df=0.5,
                                    min_df=10)
    tf = tf_vectorizer.fit_transform(word_list)

    lda = LatentDirichletAllocation(n_components=topics,#主題數
                                    learning_method='batch',
                                   #樣本量不大隻是用來學習的話用"batch"比較好,這樣可以少很多引數要調
                                    )
    #用變分貝葉斯方法訓練模型
    lda.fit(tf)

    #依次輸出每個主題的關鍵詞表
    tf_feature_names = tf_vectorizer.get_feature_names()

    return lda,tf,tf_feature_names,tf_vectorizer #返回模型以及主題的關鍵詞表
複製程式碼

最後我們將主題以視覺化結果展現出來:

#pyLDAvisUI(lda, tf, tf_vectorizer) # main 函式中最後一行
#將主題以視覺化結果展現出來
def pyLDAvisUI(lda,tf,tf_vectorizer):

    page = pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer)
    pyLDAvis.save_html(page, 'lda.html')        #將主題視覺化資料儲存為html檔案
複製程式碼

得到 lda.html ,為渲染以後的結果:

topic.gif

至此,使用者資訊分析介紹結束。

原始碼地址:github.com/starFalll/S…

參考:[1]陸飛.面向社會工程學的SNS分析和挖掘[D].上海:上海交通大學,2013.

相關文章