《生活大爆炸》(英文:The Big Bang Theory 简称:TBBT)广受喜爱(据说还可以练听力练口语blabla),去年随着第12季的播出而完结,最近也算是在补。有一天闲聊的时候偶然冒出来一个点子,就是利用词云来将大爆炸台词中的高频词汇可视化一下,也是一个有趣的练习。

简单来说我们的任务分为两部分,第一部分是获取台词数据,第二部分是生成词云和其他NLP分析。


目录

获取数据

数据源

网页分析

编写爬虫

文本分析

直接生成词云并绘制

NLP分析

分词

去停词和滤词

词干提取和词形还原

词云生成

其他NLP分析

代码与台词文本

参考资料


获取数据

数据源

首先我找到的台词数据来自于该网站,这是一位国外网友自己写了台词,并放到了自己用wordpress建的站上,目前更新到了第10季(看修改日期发现最后一个网页是在一年前更新的),有喜欢TBBT并欣赏他的工作的朋友可以去捐赠一下。

选择英文台词的原因是该剧本身台词就是英文为主,我看到的中文翻译是由YYeTs字幕组翻译的,信达雅,笑点也十分接地气,不过毕竟还是不如原文的“高频词”更有意义些。

网页分析

在谷歌浏览器中按F12使用“检查”功能,可以观察我们要爬取的网站的各项元素和它们的信息。

首先我们发现,每一季每一集的台词分别保存在各自的网页中,因此我们需要爬取多个网页,而这些网页的url都可以直接从robots.txt里的sitemap.xml里取即可(我们写爬虫时最好遵循robots.txt中所写的爬取该网站的建议,不过目前这个网站似乎并没有什么限制);

接着分析每个包含独立内容的网页中,该网站不仅包含了台词,就像剧本一样,它还包括了用斜体表示的场景信息和动作信息等,这些信息通常在台词所在标签对应的<em>标签或<i标签>下,而这部分信息我们其实是不需要的,我们可以根据这一特点只提取台词信息。注意所有台词都在<div class="entrytext">标签下,而每集又有所不同,我们发现台词的保存分为两类:

  1. 每句台词保存在<p>标签的<span>标签内,如第一季第一集
  2. 每句台词保存在<p>标签内,如第七季第二十三集

那么最后我们可以先将每集的台词独立保存,到时候只需要把对应部分的拼起来即可组成全部的台词。

编写爬虫

第一步,先利用sitemap.xml获取所有需要爬取的网页的url。这里我们先尝试使用传统的xml库中的ElementTree库来解析xml内容,通常对于一个本地文件,可以用parse方法来解析,而对于一个字符串,则可以用fromstring方法来解析,之后通过循环索引的方式获取深藏在xml中的可爬取url。

import urllib.request
import xml.etree.ElementTree as ET
response = urllib.request.urlopen(sitemap)
xml = response.read()
root = ET.fromstring(xml)  # 从parse中读出来的是tree,从fromstring中读出来的是root
urls = [child[0].text for child in root]  # 遍历root的子树并提取第一个子节点的文本内容
urls = urls[0:-3]  # 最后三个不是剧集的台词页面,而是网站的介绍页面,因此去掉

第二步,分别爬取每一集的网页并获取信息。这一步我们使用lxml库,它和传统的xml中的ElementTree库同样都是用来解析xml文档的,但lxml使用xpath语法来进行文件格式解析,更加轻便简洁。下面我们就来尝试一下,根据我们先前对网页的分析,通过xpath路径来爬取台词内容。

import urllib.request
from lxml import etree
filename = './txts/' + url[35:-1] + '.txt'  # 提取季数和集数作为单独储存的文件名
response = urllib.request.urlopen(url)
xml = response.read()
html = etree.HTML(xml)
html_data = html.xpath('//p/span/text()')  # 根据台词信息的特点获取所有台词:所有p标签下的span标签中的内容
if len(html_data) < 10:  # 如果上一种方法提取失败,就用另一种方法提取:所有div标签下的p标签中的内容html_data = html.xpath('//div/p/text()')
with open(filename,'w', encoding='UTF-8')as f:  # 保存到分文档中,换行便于阅读f.writelines([line+'\n' for line in html_data])
with open(txtname, 'a', encoding='UTF-8')as f:  # 保存到总文档中f.writelines([line+' ' for line in html_data])

由此我们就得到了每集的分集台词和总的台词语料库,可以进行文本分析了~

(由于分集台词网页的异质性,根据之前观察到的特点使用的该方法目前只提取了约四分之一的台词,后续会再逐渐尝试增加爬取更多有效内容)


文本分析

在将所有台词保存到txt文件中后,我们就可以开始文本分析的工作了,这里使用的库主要是wordcloud和nltk,分别用来进行生成词云和分词。另外绘制词云需要使用matplotlib库。

直接生成词云并绘制

事实上,由于wordcloud对英文支持较好,也包含了停用词的处理,不需要过多操作也可以绘制一个还不错的词云,如果仅仅是生成词云可以一步到位:

with open(txtname,'r', encoding='UTF-8') as f:mytext = f.read()  # 直接读取全部内容
back_coloring = imread(imgname)
image_colors = ImageColorGenerator(back_coloring)
wc = WordCloud(background_color="white",  # 背景颜色max_words=2000,  # 词云显示的最大词数mask=back_coloring,  # 设置背景图片random_state=2,margin=2  # 词语边缘像素距离)
wc.generate(mytext)
plt.figure()
plt.imshow(wc.recolor(color_func=image_colors))
plt.axis("off")
plt.show()
wc.to_file(eptname)

NLP分析

但如果不仅要生成词云,还想获得一定的自由,自主进行一些NLP的分析,那就需要手动进行一些步骤了。

NLP分析在获取语料之后,通常要进行文本预处理。英文的预处理包括:分词,去停词,词干提取和词形还原等步骤。

中文的分词相对于英文更复杂一些,也需要去停词。但没有词干提取和词形还原的需要。

分词

在nltk中,分词即意味着“token化”:

    import nltkfdist = nltk.FreqDist(nltk.word_tokenize(text))fdist.plot(30, cumulative=True)

代码第二行调用了word_tokenize()函数,此函数的作用是基于空格/标点等对文本进行分词,返回分词后的列表。如果要处理中文,需要三方的分词器先处理,之后才能使用nltk进行处理。

然而此处的分词仅仅是一个比较基本的分词,其中还保留了‘s这样的词,为了解决这些问题,我们在此之前还需要手动使用正则表达式将标点符号以及He’s之类的缩写进行替换:

import re
# to find the punctuation
pat_letter = re.compile(r'[^a-zA-Z \']+')
# to find the 's following the pronouns. re.I is refers to ignore case
pat_is = re.compile("(it|he|she|that|this|there|here)(\'s)", re.I)
# to find the 's following the letters
pat_s = re.compile("(?<=[a-zA-Z])\'s")
# to find the ' following the words ending by s
pat_s2 = re.compile("(?<=s)\'s?")
# to find the abbreviation of not
pat_not = re.compile("(?<=[a-zA-Z])n\'t")
# to find the abbreviation of would
pat_would = re.compile("(?<=[a-zA-Z])\'d")
# to find the abbreviation of will
pat_will = re.compile("(?<=[a-zA-Z])\'ll")
# to find the abbreviation of am
pat_am = re.compile("(?<=[I|i])\'m")
# to find the abbreviation of are
pat_are = re.compile("(?<=[a-zA-Z])\'re")
# to find the abbreviation of have
pat_ve = re.compile("(?<=[a-zA-Z])\'ve")new_text = pat_letter.sub(' ', new_text)
new_text = pat_is.sub(r"\1 is", new_text)
new_text = pat_s.sub("", new_text)
new_text = pat_s2.sub("", new_text)
new_text = pat_not.sub(" not", new_text)
new_text = pat_would.sub(" would", new_text)
new_text = pat_will.sub(" will", new_text)
new_text = pat_am.sub(" am", new_text)
new_text = pat_are.sub(" are", new_text)
new_text = pat_ve.sub(" have", new_text)
new_text = new_text.replace('\'', ' ')

但这时如果直接使用FreqDist类统计词频并绘制频率图,将得到许多虚词,因此需要进行停词。

去停词和滤词

所谓停用词就是那些非常常见,但没有多大信息含量的词,我们可以使用nltk自带的语料库(corpus)来去除这些停用词,在nltk.corpus下包含了一个stopword的停词库(这里需要提前下载好nltk中的stopwords):

注意这里的停用词都是小写的,所以在去除时我们需要调用lower()函数:

from nltk.corpus import stopwordsstop_words = stopwords.words('english')  # 提取停词库text_stop = [word for word in text_token if len(word.lower())>1 and (word.lower()not in stop_words)]  # 去除停词

此外可能还需要停用一些数字和名字,这时我们就需要通过词的词性来进行过滤(这里需要提前下载好nltk中的averaged_perceptron_tagger),数字对应基数标签(CD),姓名对应着单数形式的专有名词(NNP)标签:

    taggled = nltk.pos_tag(text_stop)  # 获得词性text_filtered = [word[0] for word in taggled if word[1] != 'NNP' and word[1] != 'CD']

词干提取和词形还原

统计英文词频需要考虑到词形变化,例如move和moved、is和are、dog和dogs应该归为一个词而不应该分为两个词来统计,考虑到词干提取和词形还原有部分结果是交叉的,所以这里我们只对各种形式的单词进行词形还原(lemmatization)即可:

首先需要获取词性,在NLP中,使用Parts of speech(POS)技术实现。可以先定义一个函数转换为可供词形还原函数调用的形式:

def get_wordnet_pos(tag):from nltk.corpus import wordnetif tag.startswith('J'):return wordnet.ADJelif tag.startswith('V'):return wordnet.VERBelif tag.startswith('N'):return wordnet.NOUNelif tag.startswith('R'):return wordnet.ADVelse:return None

在nltk中,可以使用nltk.pos_tag()获取单词在句子中的词性,之后使用WordNetLemmatizer()的lemmatize方法即可:

    from nltk.stem import WordNetLemmatizerwnl = WordNetLemmatizer()text_tag = nltk.pos_tag(text_filtered)  # 获取词性text_lemmatized = []for tag in text_tag:wordnet_pos = get_wordnet_pos(tag[1]) or wordnet.NOUN  # 转换词性格式text_lemmatized.append(wnl.lemmatize(tag[0], pos=wordnet_pos))  # 根据词性进行词形还原

词云生成

nltk提供的FreqDist类可以用来将单词封装成字典,并计算给定单词列表中各个单词出现的次数,而词云的wordcloud类的generate_from_frequencies方法恰好可以通过字典生成词云:

fdist = nltk.FreqDist(text_lemmatized)
wc.generate_from_frequencies(fdist)

可以看到由于包含了姓名过滤,最终的结果与直接生成词云有很大的不同。

从这一词云也能看出,大爆炸的台词很接地气,很适合用来学习英语~

其他NLP分析

fdist = nltk.FreqDist(nltk.bigrams(text_lemmatized))  # 双词统计分析
fdist = nltk.FreqDist(nltk.trigrams(text_lemmatized))  # 三词统计分析
fdist.plot(20, cumulative=True)  # 频率分布图
fdist.tabulate(20)  # 频率分布表
fdist.tabulate(20, cumulative=True)  # 频率累计表

代码与台词文本

Github项目


参考资料

Big Bang Theory Transcripts

xml.etree.ElementTree — The ElementTree XML API

python爬虫之xpath的基本使用

利用python的jieba库、wordcloud库,实现中英文文本的快速分词(代码详解版)

NLTK学习之一:简单文本分析

python数据分析(分析文本数据和社交媒体)

使用Python+NLTK实现英文单词词频统计

NLTK之词频

生活大爆炸(TBBT): 台词爬取、词云生成与NLP分析相关推荐

  1. bilibili怎么设置弹幕数量_python爬虫:bilibili弹幕爬取+词云生成

    如果你懒得看下边的文字,我录了一个完整的教学视频在b站上. 我的B站教学:https://www.bilibili.com/video/av75377135?p=2 工作原理 b站是提供弹幕接口的,所 ...

  2. python爬取bilibili弹幕_python爬虫:bilibili弹幕爬取+词云生成

    如果你懒得看下边的文字,我录了一个完整的教学视频在b站上. 我的B站教学:https://www.bilibili.com/video/av75377135?p=2 工作原理 b站是提供弹幕接口的,所 ...

  3. Python哔哩哔哩弹幕爬取+词云生成

    心血来潮,研究下词云,先上图,最终效果 代码 import numpy as np import requests from bs4 import BeautifulSoup from wordclo ...

  4. ⚡豆瓣告诉你《扫黑风暴》如何【短评爬取+词云】 ⚡

    ⚡豆瓣告诉你<扫黑风暴>如何[短评爬取+词云] ⚡ 一.前言 二.环境准备 三.具体实现 1.短评获取并保存 2.词云制作 3 成果 四.最后 ⚡豆瓣告诉你<扫黑风暴>如何[短 ...

  5. Python3.6+jieba+wordcloud 爬取豆瓣影评生成词云

    在上一篇介绍了如何通过Python爬虫抓取豆瓣电影榜单.Python3.6+Beautiful Soup+csv 爬取豆瓣电影Top250 此篇博客主要抓取豆瓣某个电影的影评,利用jieba分词和wo ...

  6. python爬取微博数据词云_用Python爬取微博数据生成词云图片

    原标题:用Python爬取微博数据生成词云图片 欢迎关注天善智能 hellobi.com,我们是专注于商业智能BI,大数据,数据分析领域的垂直社区,学习.问答.求职,一站式搞定! 对商业智能BI.大数 ...

  7. 爬虫爬取新闻并生成词云

    爬取豆瓣应用的函数同样可以用来爬取新闻.这里面主要是正则表达式的提取和对爬出的链接再进行爬取解析.对网页不 import re # 正则 from bs4 import BeautifulSoup # ...

  8. 搜狗词库爬虫(1):基础爬虫架构和爬取词库分类

    基础爬虫架构 基础爬虫框架主要包括五大模块:爬虫调度器.URL管理器.网页下载器.网页解析器.数据存储器. 爬虫调度器:启动.执行.停止爬虫,统筹其他模块的协调工作. URL管理器:管理已爬取的URL ...

  9. 爬取词库,使用jieba分词库,自定义dict.txt文件+将搜狗词库.scel文件为.txt文件

    一:爬取词库,使用jieba分词库,自定义dict.txt文件 import jiebafrom urllib.request import urlopen from bs4 import Beaut ...

最新文章

  1. 好听的歌曲---爱情转移
  2. 一个简单的三层架构例子(.NET入门)
  3. android 判断文件是否存在_每日一课 | Python检查文件是否存在
  4. javafx窗体程序_JavaFX实际应用程序:AISO HRC-Matic
  5. Linux DNS视图脑裂的实例操作(四)
  6. Python中函数的定义和参数传递
  7. K8S_Google工作笔记0001---K8S学习过程梳理
  8. php不能连接远程mysql_无法远程连接mysql怎么办
  9. .[算法]图论专题之最短路径
  10. 将网页和文档的背景改为绿色来保护眼睛
  11. centos 6.5 安装mysql rpm_centos6.5 rpm 安装mysql5.6
  12. HOI BMVC2018| iCAN: Instance-Centric Attention Network for Human-Object Interaction Detection论文阅读笔记
  13. 电子数据取证-流程与技术
  14. Cortex M3 DWT
  15. PMP 敏捷管理相关知识点
  16. FWT(快速沃尔什变换)零基础详解qaq(ACM/OI)
  17. 怎样更改计算机网络密码怎么办,wifi密码怎么改?
  18. 纯千兆电口和自适应电口的区别
  19. 量产pny的USB3.0-金棒U盘
  20. 认识VF--Visual FoxPro 漫谈(轉)

热门文章

  1. 微信5秒超时 php异步,微信小程序网络超时怎么办?
  2. 互联网券商走红的生意经
  3. 《美食街》项目(个人空间篇)
  4. 工程类评高级职称的计算机免考,技术人员评聘9类人员可免考计算机能力
  5. u盘在linux系统上只读,解决办法:
  6. 拯救者R9000X怎么用U盘重装系统方法分享
  7. Nature Outlook专题:肠道菌群研究的现在和未来 | 热心肠日报
  8. Vue3快速搭建后台管理系统
  9. 怎么改第二次微信号?分享默认微信账号的修改步骤
  10. 微信号怎么改第二次?已设置过微信号怎么改