用Python解密霍格沃茨的情感密码:哈利波特系列文本挖掘之旅

目录


在这里插入图片描述

哈利波特,这个陪伴了无数人童年和青春的名字,不仅仅是一个关于魔法、友谊和勇气的故事,更是一个情感充沛、跌宕起伏的世界。当我们挥舞魔杖,念出咒语时,是否想过这些文字背后隐藏着怎样的情感密码?作为一名AI爱好者和数据巫师,我决定用Python这根“数字魔杖”,对哈利波特系列小说进行一次彻底的文本挖掘和情感分析,让我们一起看看数据能揭示出J.K.罗琳笔下这个魔法世界哪些不为人知的情感秘密。

这不仅仅是一次技术实践,更是一次情怀之旅。准备好了吗?让我们骑上扫帚,进入Python的魔法课堂!

1. 当魔法遇到代码:Python揭秘哈利波特的情感宇宙

在数据科学的武器库中,Python因其庞大的生态系统、易用性和强大的库支持,在文本挖掘领域大放异彩。本次冒险,我们将主要依赖以下几个核心Python库:

  • pandas: 数据处理和分析的基石,提供高效的DataFrame结构。
  • nltk (Natural Language Toolkit): 强大的自然语言处理库,用于文本分词、停用词去除等。
  • matplotlibseaborn: 流行的可视化库,用于绘制各种图表。
  • (可选)textblob: 一个简化文本处理的库,内置简单的情感分析功能。
  • 情感词典文件:我们将需要手动加载或从网络获取情感词典(如AFINN, Bing, NRC)。

我们的目标是:提取哈利波特系列书籍中的文本,利用情感词典分析情感倾向,并可视化这些情感在故事中的分布和变化。这就像用“显形咒”让隐藏的情感脉络清晰可见。

首先,确保你已安装必要的库:

pip install pandas nltk matplotlib seaborn requests

你可能还需要下载NLTK的一些资源(如分词器punkt和停用词表stopwords):

import nltk
try:
    nltk.data.find('tokenizers/punkt')
except nltk.downloader.DownloadError:
    nltk.download('punkt')
try:
    nltk.data.find('corpora/stopwords')
except nltk.downloader.DownloadError:
    nltk.download('stopwords')

2. 魔法世界的基石:数据准备与预处理 (Python版)

冒险的第一步,是收集和整理我们的“魔法原材料”——书籍文本。Python中我们通常需要自行准备文本数据。

假设你已经有了7本书的文本,并且每本书的内容被组织成一个章节列表。

import pandas as pd
from nltk.tokenize import word_tokenize
import re # 用于简单的文本清理

# 书籍标题,按出版顺序
titles = ["Philosopher's Stone", "Chamber of Secrets", "Prisoner of Azkaban",
            "Goblet of Fire", "Order of the Phoenix", "Half-Blood Prince",
            "Deathly Hallows"]

# --- 数据加载模拟 ---
# 在真实场景中,你需要从文件加载这些文本
# 这里我们用非常简短的占位符文本来模拟
# 每一本书是一个章节列表,每个章节是一个字符串
example_texts = {
   
    "Philosopher's Stone": [
        "Mr. and Mrs. Dursley, of number four, Privet Drive, were proud to say that they were perfectly normal, thank you very much. They were the last people you'd expect to be involved in anything strange or mysterious, because they just didn't hold with such nonsense.",
        "Harry had a thin face, knobbly knees, black hair, and bright green eyes. He wore round glasses held together with a lot of Scotch tape because of all the times Dudley had punched him on the nose. The only thing Harry liked about his own appearance was a very thin scar on his forehead that was shaped like a bolt of lightning."
    ],
    "Chamber of Secrets": [
        "Not for the first time, an argument had broken out over breakfast at number four, Privet Drive.",
        "Harry was used to spiders, because the cupboard under the stairs was full of them, and that was where he slept."
    ],
    # ... 为简洁起见,省略其他书籍的模拟文本
    # 你需要为所有7本书提供真实的章节文本
}
# 为了让示例能运行,我们为其他书也创建简短的占位符
for title in titles:
    if title not in example_texts:
        example_texts[title] = [f"This is chapter 1 of {
     title}.", f"This is chapter 2 of {
     title}. Danger and magic await!"]

# books_data 将是一个列表,每个元素是一个包含章节文本的列表
books_data = [example_texts.get(title, []) for title in titles]
# --- 数据加载模拟结束 ---


all_words_list = [] # 初始化一个空列表用于存储所有词的数据

# 循环处理每本书
for i, book_title in enumerate(titles):
    book_chapters = books_data[i] # 获取当前书的章节列表
    for chap_num, chapter_text in enumerate(book_chapters):
        # 1. 分词 (Tokenization)
        #    我们转换为小写,并只保留字母组成的词
        words = word_tokenize(chapter_text.lower())
        cleaned_words = [word for word in words if word.isalpha()] # 只保留字母词
        
        # 2. 为每个词创建记录
        for word in cleaned_words:
            all_words_list.append({
   
                'book': book_title,
                'chapter': chap_num + 1, # 章节号从1开始
                'word': word
            })

# 将列表转换为Pandas DataFrame
series_df = pd.DataFrame(all_words_list)

# 将书名转换为分类类型,并按原始顺序排序,便于后续绘图
# Pandas的Categorical类型类似于R的factor
series_df['book'] = pd.Categorical(series_df['book'], categories=titles, ordered=True)

# 查看处理后的数据结构
print(series_df.head())
print(f"\nTotal words processed: {
     len(series_df)}")

代码解读:

  1. titles: 定义书名列表。
  2. example_textsbooks_data: 这是数据加载的关键部分。这里我们使用了一个字典 example_texts 来模拟章节化的文本数据。在实际应用中,你需要替换这部分代码,使其能从你的文本文件(如.txt)中读取内容,并将每本书的文本分割成章节(如果原始数据是这样组织的)。books_data 随后被构建成一个列表的列表,外层列表代表书籍,内层列表代表该书的章节文本。
  3. all_words_list: 初始化一个空列表,用于收集所有词汇及其元数据(所属书籍、章节)。
  4. for 循环遍历书籍和章节:
    • word_tokenize(chapter_text.lower()): 使用nltkword_tokenize函数将章节文本分割成词汇,并转换为小写。
    • cleaned_words = [word for word in words if word.isalpha()]: 进行简单的清洗,只保留由字母组成的词汇,移除非字母标记(如标点符号)。更复杂的预处理可能包括去除停用词(stopwords)、词形还原(lemmatization)等。
    • all_words_list.append(...): 为每个清洗后的词创建一个字典,包含书名、章节号和词本身,然后添加到列表中。
  5. series_df = pd.DataFrame(all_words_list): 将包含所有词汇信息的列表转换为Pandas DataFrame。这个DataFrame的结构类似于R代码中的series tibble,每一行代表一个词。
  6. series_df['book'] = pd.Categorical(...): 将book列转换为Pandas的Categorical类型,并指定了类别的顺序(categories=titles)。这确保了在后续绘图时,书籍能按照出版顺序排列。

现在,series_df DataFrame包含了哈利波特全系列(模拟数据)每一本书、每一章的每一个词。这为我们后续的情感分析打下了坚实的基础。

原始书籍文本 List of Chapters per Book
Loop through Books & Chapters
nltk.word_tokenize: 文本按词拆分
Lowercase & Clean Words
Store word with Book & Chapter metadata
Pandas DataFrame `series_df` 每行一词

图1: Python数据准备流程图

3. 情感的度量衡:认识我们的情感词典 (Python版)

要进行情感分析,我们需要情感词典。在Python中,我们可以加载常见的词典文件,或者使用像VADER这样内置词典的库。这里我们将演示如何加载AFINN, Bing和NRC词典。你需要从网络上下载这些词典文件,或者使用下面提供的URL。

import pandas as pd
import requests # 用于从URL下载文件
from io import StringIO # 用于读取文本内容

# --- AFINN 词典 ---
# AFINN为词汇分配一个从-5(极消极)到+5(极积极)的整数分数。
afinn_url = "https://raw.githubusercontent.com/fnielsen/afinn/master/afinn/data/AFINN-111.txt"
try:
    response = requests.get(afinn_url)
    response.raise_for_status() # 检查请求是否成功
    afinn_lexicon = pd.read_csv(StringIO(response.text), sep='\t', header=None, names=['word', 'value'])
    print("AFINN Lexicon loaded successfully:")
    print(afinn_lexicon.head())
except requests.exceptions.RequestException as e:
    print(f"Error loading AFINN lexicon: {
     e}")
    afinn_lexicon = pd.DataFrame(columns=['word', 'value']) # 创建空DataFrame以防出错

# --- Bing Liu 词典 ---
# Bing将词汇分为“positive”和“negative”两类。
bing_positive_url = "https://ptrckprry.com/course/ssd/data/positive-words.txt"
bing_negative_url = "https://ptrckprry.com/course/ssd/data/negative-words.txt"
bing_lexicon_list = []
try:
    # Positive words
    response_pos = requests.get(bing_positive_url)
    response_pos.raise_for_status()
    # Skip comment lines starting with ;
    pos_words = [word for word in response_pos.text.splitlines() if not word.startswith(';')]
    for word in pos_words:
        if word.strip(): # Ensure word is not empty
            bing_lexicon_list.append({
   'word': word.strip(), 'sentiment': 'positive'})
    
    # Negative words
    response_neg = requests.get(bing_negative_url)
    response_neg.raise_for_status()
    neg_words = [word for word in response_neg.text.splitlines() if not word.startswith(';')]
    for word in neg_words:
        if word.strip():
            bing_lexicon_list.append({
   'word': word.strip(), 'sentiment': 'negative'})
            
    bing_lexicon = pd.DataFrame(bing_lexicon_list)
    print("\nBing Lexicon loaded successfully:")
    print(bing_lexicon.head())
    print(bing_lexicon.tail())
except requests.exceptions.RequestException as e:
    print(f"Error loading Bing lexicon: {
     e}")
    bing_lexicon = pd.DataFrame(columns=['word', 'sentiment'])

# --- NRC 词典 ---
# NRC将词汇分为多种情感类别以及positive/negative。
# NRC Emotion Lexicon: NRC-Emotion-Lexicon-Wordlevel-v0.92.txt
# Format: word<TAB>emotion<TAB>association (1 if associated, 0 if not)
nrc_url = "https://raw.githubusercontent.com/jeffreybreen/twitter-sentiment-analysis-tutorial-201107/master/data/emotions-lexicon/NRC-Emotion-Lexicon-Wordlevel-v0.92.txt"
nrc_lexicon_list = []
try:
    response = requests.get(nrc_url)
    response.raise_for_status()
    lines = response.text.splitlines()
    for line in lines:
        parts = line.split('\t')
        if len(parts) == 3:
            word, emotion, association = parts[0], parts[1], int(parts[2])
            if association == 1: # Only include words associated with the emotion
                nrc_lexicon_list.append({
   'word': word, 'sentiment': emotion})
    nrc_lexicon = pd.DataFrame(nrc_lexicon_list)
    print("\nNRC Lexicon loaded successfully:")
    print(nrc_lexicon.head())
except requests.exceptions.RequestException as e:
    print(f"Error loading NRC lexicon: {
     e}")
    nrc_lexicon = pd.DataFrame(columns=['word', 'sentiment'])

代码解读:

  • 我们使用 requests 库从URL获取词典的文本内容,并用 StringIO 将其包装成文件类对象,以便 pandas.read_csv (AFINN) 或手动解析。
  • AFINN: 直接是一个制表符分隔的文件,包含词和对应的情感分数。
  • Bing: 分为积极词和消极词两个文
### 使用Python Turtle库制作哈利波特主题的绘图 #### 创建霍格沃茨城堡轮廓 为了创建一个简单的霍格沃茨城堡轮廓,可以定义一系列命令来描绘建筑的主要结构。 ```python import turtle def draw_hogwarts(): castle = turtle.Turtle() castle.speed('fastest') # 绘制主塔楼 castle.penup() castle.goto(-150, -100) castle.pendown() castle.begin_fill() for _ in range(4): castle.forward(300) castle.left(90) castle.end_fill() # 添加细节如窗户和门 # 这里仅展示基础框架 draw_hogwarts() turtle.done() ``` 此代码片段展示了如何构建基本矩形作为城堡主体[^1]。对于更复杂的特征比如尖顶、旗帜或是特定装饰,则需进一步细化函数逻辑并增加更多形状组合。 #### 制作魔法扫帚飞行动画 通过调整`forward()`方法中的参数以及利用`towards()`, `goto()`等定位方式可以让虚拟角色沿着预定轨迹移动模拟扫帚飞行效果。 ```python import time from random import randint import turtle as t def fly_broomstick(start_x=-200, start_y=0): broom = t.Turtle(shape="arrow") # 设置箭头代表扫帚前端 broom.color("brown") broom.shapesize(stretch_wid=.5, stretch_len=3) end_positions = [(randint(-180, 180), y) for y in range(int(start_y)+1, 200)] for pos in end_positions: broom.setheading(broom.towards(pos)) while round(broom.xcor(), 1) != pos[0] or round(broom.ycor(), 1) != pos[1]: broom.fd((pos[0]-broom.xcor())*.05+(pos[1]-broom.ycor())*.05) time.sleep(.01) fly_broomstick() t.mainloop() ``` 上述脚本实现了从左下角向右上方随机位置平滑过渡的效果,模仿了骑乘飞天扫帚的动作[^3]。注意这里使用了一个简化版的距离计算公式使运动更加流畅自然。 #### 展现闪电疤痕标志 考虑到哈利·波特最著名的个人标记——额头上的闪电伤疤,可以通过圆弧与直线相结合的方式轻松再现这一经典元素。 ```python import math import turtle def lightning_scar(turt=turtle.Pen()): radius = 50 angle = 60 sides = int(radius * math.pi / (angle / 180)) turt.penup(); turt.goto(0,-radius); turt.pendown(); turt.circle(radius, extent=(angle*2)) # 半圆形部分 turt.right(angle//2) for i in range(sides): # 中间锯齿状线条 turt.forward(10*(i%2+1)/sides) turt.left((-1)**i * angle/sides) lightning_scar() turtle.exitonclick() ``` 该程序先画出了半个圆周再接续几条短折线形成类似闪电形态的设计。这不仅能够很好地表示出故事里的标志性记号,同时也提供了一种练习几何图形绘制的好机会。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海棠AI实验室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值