python十角星_使用 Python 绘制《星战》词云

作者介绍

Rafael Schultze-Kraft

“前神经科学家,数据挖掘及机器学习的狂热爱好者,Python 的狂热粉丝”

使用 Python 绘制《星战》词云

当前我们在 Jupyter Notebook 中推进的是一个有趣的小项目,它的目标是将星球大战中的角色可视化为词云 —— 也就是说,从星战系列 1-6 部中的角色台词出发,找到最能刻画他们的人物特征的词语。

因此,我爬取了星战系列 1-6 部的电影剧本,提取出星战中每个角色的台词内容,并经过进一步的加工处理,对每一个人物角色进行词云可视化。

本文介绍了一些基本的数据科学概念,演示了一些 Python 库的使用,包括了爬虫库(requests 和 beautifulSoup)、自然语言处理库(NLTK、string 和 re)、词云库(Andreas Muller 的 wordcloud),以及可视化库(matplotlib)。

电影剧本的数据来源是 IMSDb(该数据库仅可用于教育目的)。

你能在我的 GitHub(https://github.com/neocortex/mining-star-wars) 上找到本文的所有代码。其实,我早在 2016 年的 EuroPython 大会上分享过此项目实例。

让我们开始着手做吧!

(下面的第一条代码纯粹是为了避免在 notebook 输出时出现警告内容,影响美观)

导入必备模块

首先,我们需要导入一些后文中一定会用到的库。正如前文所说,为了爬取包含电影剧本的 HTML 页面,我会结合使用 requests 和 BeautifulSoup,requests 用来发起 HTTP 请求,而 BeautifulSoup 用来解析 HTML 页面,并提取出我们想要的内容。另外,我们还需要导入一些辅助性的库(比如 collections),用来处理字符的库(re、string 和 nltk),用来画图的 matplotlib 库,以及用来生成词云的 wordcloud 库。

数据来源

正如前文中提到的,我会把 IMDSb (http://www.imsdb.com/ )作为我们的数据源。下面我将建立一个字典,用来存储星战系列电影名称及其对应的数据库链接。

抓取数据

星战系列之四 —— 新希望

我会按照电影上映的日期来进行爬取,因此我首先爬取星战系列中的第四部。我们只需要简单地构造一个包含目标链接的 GET 请求,就可以得到电影剧本了。得到的响应内容则会被转换为一个 BeautifulSoup 对象。

HTML 页面源码混合了文本、标签、JavaScript 脚本等内容。幸运的是,BeautifulSoup 能帮助我们解析这些 HTML 页面,从而取得我们想要的文本信息。 因此,为了得到电影中每个人物的台词,我们需要查看 HTML 的结构,看看怎样才能提取出我们所需要的内容。下图是星战第四部剧本的部分 HTML 页面截图。

在快速查看 HTML 源码后,我发现,嵌入了人物名称(比如 THREEPIO)的 标签下紧跟着的便是对应人物的台词内容。然而,我发现,人物对话发生的位置场景描述(比如,INT. REBEL BLOCKADE RUNNER)也以同样的结构被放在了 HTML 源码中。两者的区别在于,包含人物名称的 标签缩进得更多(实际上确切来说,缩进了 37 个空格)。因此,我采用的爬取方式是遍历 soup 中的所有 标签,并且检查该 标签是否至少缩进了 37 个空格(否则该 标签下的文本内容应该是位置场景描述),若是的话,我们便只需要提取出该 标签的闭合标签后紧跟的文本内容,就能得到人物台词了。

最后,我遍历了对话的每一行(直至遇到空行),由于以 “(” 开头的句子是描述性的内容,和对话中的人物台词无关,所以我还需要过滤掉它们。

听起来好像有点复杂,但实际上真的挺简单的。代码如下:

爬取出来的结果是以字典格式存储的,形如,{角色名称:台词}。这能让我们得到星战四中台词数排名前十的人物角色:

下面给出字典中一个角色(Darth Vader)台词的示例:

目前一切都进展得很顺利!让我们接下来看看星战五的情况:

星战系列之五 —— 帝国反击战

系列之五的爬取和上面的爬取方法基本一致,唯一的区别在于,位置场景描述和人物角色对话处的 标签的缩进数,与第四部中的不一样。在第五部中,没有用空格缩进,而是用 tab 来缩进的。除此之外,对话的提取方法和上文是完全一致的。

第五部中台词数排名前十的人物角色是:

让我们再来看看星战系列之六的情况。

星战系列之六 —— 绝地归来

在《绝地归来》中,提取对话的方式和之前几部的有些不一样。同样地, 标签之间既包含了角色对话,也包含了对位置场景的描述。然而,区分两者的方式发生了变化:位置场景描述的前面多了一个数字(对话格式依然保持不变)。

因此,我们可以简单地遍历所有 标签,检查其中嵌套的文本内容是否始于数字,如果不是始于数字,则表明该内容是角色名称,那么接下来的步骤和前面几部的是一样的。

星战系列之一 —— 幽灵的威胁

星战一的文本提取方法与之前的方法不同。通过查看 HTML 源码,我们发现了剧本内容被写入了

 标签中。因此,第一步就是把这些内容提取出来。

我们需要遍历上面提取出来的

 节点的所有孩子节点,才能提取出对话内容。通过识别双换行符 \n\n 来拆分文本,我们可以得到这些孩子节点中的文本所包含的每个段落。

另外,角色名称后面跟着一个冒号表示这是一段对话,因此,如果我们利用冒号对文本段落再次进行拆分,若分裂出来的只有一个部分,则我们可以认为该段落不属于对话。最后,为了确保当前行是一段对话,我们还需要检查拆分出来的第一部分是否是大写的(即角色名称),并且移除括号里的文本。

星战系列之二 —— 克隆人的进攻

《克隆人的进攻》的对话提取方法和星战五的方法相似。我再次遍历所有 标签,并确保文本缩进长度等于 4。基于此,对于 标签的闭合标签后的内容的每一行,我还检查了其缩进数是否正好为 3,这些判断确保了该行的确属于对话内容(而不是其他剧本内容)。 注:这些提取特点都能通过查看 HTML 源码得知。

星战系列之三 —— 西斯的复仇

已经到了最后一部啦!在查看 HTML 源码后,对话文本的提取在这里开始变得相当简单:我们简单地遍历

标签下的每一行(使用双换行符 \n\n 来拆分文本)—— 如果该行包含一个冒号,并且拆分出来的第一部分是大写的,则当前行即为对话文本...以上便是我们所需要完成的步骤。

合并分析

我们已经得到了从每部剧集中提取出来的对话内容,接下来,我们会将这些对话内容整合到单个字典中。老实说,我们可以从一开始就使用单个字典来存储这些剧集中的对话内容。我没有这样做的原因在于,考虑到有人可能想要分析这些剧集之间的关系。

最后,我们列出了对话中台词数排名前 100 的角色名称:

细心的读者可能会发现,实际上属于同一角色的台词条目,却被划分给了“多个”角色,比如,“BEN” 和 “BEN'S VOICE”,以及 “PADM\xc9” 和 “PADME”。因此,接下来,我定义了一个函数,将这些台词条目合并给同一个角色。并且我决定将系列一至系列三中的 OBI-WAN,和系列四至系列六中的 BEN 当成两个独立的角色。

同样地,通过对各角色在所有剧集中的台词总数进行排序,我们能得到排名前 20 的角色:

进一步的文本预处理

为了对文本进行进一步的预处理,以及生成词云,我选取了下面存在感比较强的角色:

停止词

下一步我们需要做的是,移除提取出来的文本中的停止词。为了达到目的,我使用三个不同的停止词词源:其中两个是我用谷歌搜索到的英文停止词词源,剩下的那个是 Python 的 NLTK 库(http://www.nltk.org/)自带的停止词词库。

清洗文本

文本的清洗步骤如下:移除标点符号

给角色名称添加标点符号(例如,“r2 d2” 应该被识别为 “r2-d2”)

移除特殊的字符,比如 tab 和换行符

移除停止词,并把文本拆分成单词列表

让我们看看经清洗后的文本长什么样子(以 Darth Vader 为例):

词云

是时候生成词云了! 为了达到目的,对于 cleaned 字典中的每个角色而言,我们需要先计算其对应的每个单词的词频(使用 collections库中的 Counter),然后将结果输入到 wordcloud 库中(这就和用单词、频率列表来实例化 WordCloud 类一样简单 —— 事实上,你还可以使用纯文本)。我们接下来会使用 matplotlib 库来展现词云。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值