從NLP視角看電視劇《狂飆》,會有什麼發現?

2023-03-02 21:01:22

關鍵詞: 爬蟲、文字資料預處理、資料分析、視覺化、自然語言處理

摘要: 本文主要內容,獲取解析豆瓣《狂飆》的短評相關資料和演職員資訊,在資料預處理後,進行簡單的資料分析和視覺化展示。

本文全部程式碼路徑: https://github.com/fengxi177/pnlp2023/tree/main/chapter_1


1、背景

前文 文字資料預處理:可能需要關注這些點 分享了關於文字預處理的理論知識,本文將分享一份範例demo。正好,碰到了熱議的電視劇《狂飆》。因此,本文打算從自然語言處理、資料分析和視覺化的角度來湊個熱鬧(原本計劃在大結局當天發出來文章,可惜,大結局有一段時間了。拖延了,哈哈哈)。

2、資料獲取

既然要做電視劇《狂飆》相關的nlp資料分析,那麼就先選定資料目標站。經過一圈搜尋對比,發現還是豆瓣中的評論更為客觀,參與群體數量多,見解更豐富專業,哈哈哈。因此,本文將獲取 https://movie.douban.com/subject/35465232/ 頁面中的相關資料。

截止2023年2月28日,豆瓣中電視劇《狂飆》的短評已經22w+(2023年2月6日13w+,評論熱度依然很高)。通過翻看短評資料,可以發現不登入狀態最多可以獲取220條資料,登入後最多可以獲取600條資料。一般,可以通過cookie和selenium的方式實現登入,網上有參考教學,自行蒐集。

不過,在不登入狀態下,通過URL引數設定分析,發現各引數下都可以獲得220條資料。因此,本文只獲取不登入狀態下的資料。具體的,通過好評、中評和差評引數percent_type設定分別獲取220條短評及其相關資料。(特別的,仔細觀察URL的引數設定還可以獲得更多的資料哦。)

點選檢視爬蟲程式碼
def parse_comments(url):
    """
        解析HTML頁面,獲得評論及相關資料
    :param url:
    :return:
    """
    html = get_html(url)
    soup_comment = BeautifulSoup(html, 'html.parser')

    # 所有獲取的一頁資料
    data_page = []

    # 提取評論
    comments_all = soup_comment.findAll("div", "comment-item")
    for comments in comments_all:
        try:
            # 解析評論及相關資料
            comment_info = comments.find("span", "comment-info")  # 評論id相關資訊
            comment_vote = comments.find("span", "comment-vote")  # 評論點贊資訊
            comment_content = comments.find("span", "short").text.replace("\n", "")  # 評論內容

            # 提取需要的各欄位資訊
            info_list = comment_info.findAll("span")
            star_rating = info_list[1]

            user_name = comment_info.find("a").text
            video_status = info_list[0].text  # 電視劇觀看狀態
            comment_score = int(star_rating["class"][0][-2:])  # 評論分值
            comment_level = star_rating["title"]  # 評論等級
            comment_time = info_list[2].text.replace("\n", "").replace("    ", "")  # 評論時間
            # print(info_list)
            comment_location = info_list[3].text  # 評論位置

            comment_vote_count = int(comment_vote.find("span", "votes vote-count").text)  # 評論被點贊數

            # 獲取的一條資料
            # ["使用者名稱", "電視劇觀看狀態", "評論分數", "評論等級", "評論時間", "評論位置", "評論點贊數", "評論"]
            data_row = [user_name, video_status, comment_score, comment_level,
                        comment_time, comment_location,
                        comment_vote_count, comment_content]
            data_page.append(data_row)
        except:
            # 跳過解析異常的資料
            continue

    return data_page

完整程式碼:請檢視get_comments_data.py檔案

此外,本文還獲取了《狂飆》的演職員資訊資料,頁面解析的程式碼片段如下。

點選檢視程式碼

    html = get_html(url)
    soup_info = BeautifulSoup(html, 'html.parser')

    # 獲得的結果資訊
    result_info_dict = {}

    # 提取評論
    info_all = soup_info.findAll("div", "info")
    for info in info_all:
        info_name = info.find("span", "name").text
        info_role = info.find("span", "role").text
        info_works_list = info.find("span", "works").findAll("a")

完整程式碼:請檢視get_celebrity_info.py檔案。

3、文字分析與視覺化

3.1 短評資料預處理

文字資料預處理的詳細介紹,可以參考文章文字資料預處理:可能需要關注這些點。在實際的應用分析中,資料預處理並不是等資料完全收集完畢後一蹴而就的。通常,在合適的時候進行必要的處理是十分必要的,比如本文在解析爬取資料的時候會進行一些替換和資料轉換操作。

3.2 詞雲圖視覺化

詞雲圖作為一種直觀、簡潔、易於理解的資料視覺化方法,通過詞雲圖文字大小、顏色、字型等方式的展示,人們可以迅速瞭解文字資料中的關鍵詞和主題等有用資訊。

本文利用pyecharts生成短評的詞雲圖,其他也可以通過wordcloud包繪製詞雲圖。特別的,可以通過背景圖設定生成各種形狀的詞雲圖。

3.3 top關鍵詞共現矩陣網路

文字中關鍵詞是很重要的特徵,關鍵詞共現矩陣網路是一組文字中詞或短語之間的共現關係網。該網路可以幫助我們發現文字中的潛在主題、領域和關聯性,也可以用於文字資料視覺化和分析。共現網路中,每個關鍵詞被表示為一個節點,詞之間的共現關係被表示為邊,關鍵詞之間的共現頻率表示權重。我們可以使用網路分析演演算法挖掘文字中的相關主題和模式。

利用pyecharts視覺化短評top 2000關鍵詞的詞共現結果如圖所示。

Gephi是一個常用的網路分析和視覺化軟體,本文同時用gephi視覺化了一組top 2000關鍵詞的詞共現關係圖如下。

Gephi視覺化結果

3.4 《狂飆》演職員圖譜構建

知識圖譜是一種將實體、屬性、關係等知識以圖譜的形式進行表示和儲存的技術,可以幫助人們更加直觀地瞭解知識的關聯和組織方式。在影視、音樂、文學等領域,知識圖譜也被廣泛應用於作品分析、人物關係探究方面。

知識圖譜的構建需要經過多個階段,包括實體識別、關係抽取、實體連結等步驟。本文通過爬取《狂飆》的演職員資訊,進行資料淨化和處理後,使用pyecharts構建了一個包含演員、導演、編劇、代表作、《狂飆》中的飾演人物等實體,以及他們之間關係的《狂飆》演職員知識圖譜,用於展示演職員、作品及飾演人物之間的關係。通過圖譜關係展示,可以直觀的瞭解到演員、導演、編劇等之間的合作關係。這些關係的分析可以幫助我們更好地瞭解影視行業的人際關係網路,感興趣的朋友可以繼續擴充套件該圖譜,探索更多的應用場景。

《狂飆》演職員關係圖譜(全部)

《狂飆》演職員關係圖譜(姓名->角色)

《狂飆》演職員關係圖譜(姓名->代表作)

圖譜構建的程式碼如下:

點選檢視程式碼

def generate_celebrity_graph():
    """
        構建演職員關係圖譜
    :return:
    """
    df = pd.read_csv("./data/狂飆演職員資訊表.csv")
    data = df.values.tolist()

    # 轉換格式
    nodes = []
    links = []
    nodes_name = []

    symbolSize_dict = {"姓名": 30, "角色": 20, "飾演人物": 20, "代表作": 20}
    categories = [{"name": x} for x in symbolSize_dict.keys()]

    for row in data:
        # 姓名、角色(";"分割多個)、飾演人物(可能為空)、代表作(";"分割多個)
        name, role, role_to_play, works = row
        role_list = role.split(";")
        works_list = works.split(";")

        if name not in nodes_name:
            nodes_name.append(name)
            # 一個節點
            node = {
                "name": name,
                "symbolSize": symbolSize_dict["姓名"],
                "category": "姓名",
            }
            nodes.append(node)

        for role_temp in role_list:
            if role_temp not in nodes_name:
                nodes_name.append(role_temp)
                node = {
                    "name": role_temp,
                    "symbolSize": symbolSize_dict["角色"],
                    "category": "角色",
                }
                nodes.append(node)

            link = {
                "source": name,
                "target": role_temp
            }
            links.append(link)

            if role_temp == "演員":
                if role_to_play not in nodes_name:
                    nodes_name.append(role_to_play)
                    node = {
                        "name": role_to_play,
                        "symbolSize": symbolSize_dict["飾演人物"],
                        "category": "飾演人物",
                    }
                    nodes.append(node)

                link = {
                    "source": name,
                    "target": role_to_play
                }
                links.append(link)

        for works_temp in works_list:
            if works_temp not in nodes_name:
                nodes_name.append(works_temp)
                if works_temp == "狂飆":
                    node = {
                        "name": works_temp,
                        "symbolSize": 50,  # 特別設定
                        "category": "代表作",
                    }
                else:
                    node = {
                        "name": works_temp,
                        "symbolSize": symbolSize_dict["代表作"],
                        "category": "代表作",
                    }
                nodes.append(node)

            link = {
                "source": name,
                "target": works_temp
            }
            links.append(link)

    c = (
        Graph(init_opts=opts.InitOpts(theme=ThemeType.CHALK, width="1500px", height="1000px"))
        .add(
            "",
            nodes,
            links,
            categories,
            repulsion=1000,
            linestyle_opts=opts.LineStyleOpts(curve=0.6),
        )
        .set_global_opts(
            legend_opts=opts.LegendOpts(pos_left=100, pos_top=350, orient="vertical"),
            title_opts=opts.TitleOpts(title="人物關係圖譜"),
        )
        .render("./result/演職員圖譜.html")
    )
    print("演職員關係圖譜,儲存路徑為:./result/演職員圖譜.html")

4、短評相關資料分析與視覺化

在獲取評論的時候,順便獲取了關於評分、評論時間、評論位置和評論點贊數等相關資料。本文對評論位置與評論數量進行了統計分析,並將結果利用pyecharts進行了視覺化展示。由柱狀圖可以直觀看到獲取評論資料量與地域之間的分佈。此外,如感興趣,還可以對「評分與時間」、「評分與位置」、「評分與點贊數」等關係進行分析,繪製折線圖、餅圖、地圖等視覺化效果。

5、總結

本文通過獲取和解析豆瓣電視劇《狂飆》的短評和演職員資訊,對這部電影進行了簡單的資料分析和視覺化展示。感興趣的朋友,可以繼續發散思維、擴充套件資料,探索發現更多的資料分析和視覺化結果。


歡迎關注微信公眾號:實用自然語言處理

原文連結:https://mp.weixin.qq.com/s/nURcYKN6vRBKjbMXAUbEng