Python jieba分词实例:对《西游记》进行分词(上)

本节教程,我们将学习如何使用 jieba 模块来实现古典名著《西游记》的分词,并且会将重点人物出场次数以图形化的方式显示出来,并进一步创建一个词云图。

读取文件

因为小说《西游记》的内容非常长,我们不太可能会把它放到一个字符串中来操作,所以我们需要它保存在一个文件中。那么我们就需要操作整个文件,把文件中的内容读取出来。我们操作文件的流程是:

1.打开文件,得到文件句柄并赋值给一个变量;

2.通过句柄对文件进行操作;

3.关闭文件。

打开文件就要用到 open() 函数。其实,我们在之前介绍了打开文件的方法,如果忘了可以再次查看学过的内容回顾一下。

《西游记》的分词

在前面,我们已经介绍了如何使用 jieba 库分词,以及如何打开一个文本文件。接下来,我们要对经典小说《西游记》进行分词,并且把出现频率最高的词语展示出来。

首先,把《西游记》保存到一个文本文件中,要注意的是,保存文件的时候,要将编码格式选择为 UTF-8,否则读取文件的时候会报错。此外,我们要把这个文本文件放到和程序代码所在位置相同的文件夹中,这样就不需要指定路径了。

我们先来看一下用于分词的程序代码,代码如下。
import jieba
def takeSecond(elem):
    return elem[1]
def main():
    path = "西游记.txt"
    file = open(path,"r",encoding="utf-8")
    text=file.read()
    file.close()
    words = jieba.lcut(text)
    counts = {}
    for word in words:
        counts[word] = counts.get(word,0) + 1
    items = list(counts.items())
    items.sort(key = takeSecond,reverse=True)   
    for i in range(20):
        item=items[i]
        keyWord =item[0]
        count=item[1]
        print("{0:<10}{1:>5}".format(keyWord,count))
main()

因为要使用 jieba 的函数,所以这里首先需要导入 jieba 模块。
import jieba

接下来,我们定义了两个函数:main(主体函数)和 takeSecond(用于获取列表的第 2 个元素)。然后,定义了变量 path 来保存相对路径。使用 open() 函数以只读方式打开文本文件“西游记.txt”,指定的编码方式是 UTF-8,并且将文件句柄赋值给变量 file。随后调用 read() 方法读取文件中的内容并保存到变量 text 中。调用 close() 方法关闭文件。

然后我们使用 jieba.lcut() 方法对变量 text 中的内容进行分词,并且把分词的结果列表保存到变量 words 中。我们新建一个叫作 counts 的字典。然后,通过一个循环语句,遍历列表 words 中的每个元素,用变量 word 来表示每个元素。

在循环中,把 word 作为字典 counts 的键,把 get() 方法返回的值加上 1,作为这个键所对应的值。这表示每次遇到同样的键,都会让它的值加上 1(以统计相同的键的数目)。需要注意,如果在字典中没有找到键所对应的值,那么 get() 方法会返回默认值 0。当循环结束后,字典 counts 就包含了西游记中拆分出来的全部词语以及对应的该词语出现的次数。

接下来,我们想要按照词的出现次数排序。之前我们介绍字典的时候曾经提到,字典是没有办法排序的,我们需要把字典转换为列表,然后利用列表的 sort() 方法来排序。因为我们是使用人物的出现次数来排序的,所以要给 sort() 方法传递一个 key 参数,以指定用来进行比较的元素,该元素就是取自于可迭代对象中。

这里调用了自定义的 takeSecond() 函数。这个函数接收的参数是一个列表,返回的是这个列表的第 2 个元素。这样,我们就可以指定第 2 个元素进行逆序排序,并且把结果赋值给 items。

然后,借助 range() 函数生成一个等差数组,展示 items 中前 20 个元素。在每次循环中,我们先把获取的元素赋值给变量 item。然后把 item 的第 1 个列表元素赋值给变量 keyWord,第 2 个元素赋值给变量 count。

然后使用 print() 方法把格式化后的两个变量输出到屏幕上。这里我们用到了 format() 方法,它可以按照需求来格式化字符串。代码的含义是把 keyWord 的值左对齐,宽度是 10;把 count 的值右对齐,宽度是 5。

字符串的 format() 方法可以用来格式化字符串。format() 方法通过字符串中的花括号 {} 来识别要替换的内容,而 format() 中的参数是要填入的内容,按照顺序进行匹配。花括号中冒号后后面的 < 符号表示左对齐,> 符号表示右对齐,数字表示宽度。

然后调用 main() 函数。最终得到的词频统计结果如图 1 所示


图 1

我们发现一个问题,大部分的词语都是一个字。也就是没有把长度为 1 的词语进行筛选。下面,我们继续优化这个程序。

筛选长度为 1 的词语

因为我们没有对分词的结果进行筛选,所以前 20 个高频词语大多是一个字,而这显然不是我们想要的结果。因为一个字的词语没有太多的含义,而我们需要的是有意义的词语,所以接下来,我们介绍如何将长度为 1 的字的词语过滤掉。

为了便于区分,我们将新增的代码突出显示出来,代码如下。
import jieba
def takeSecond(elem):
    return elem[1]
def main():
    path = "西游记.txt"
    file = open(path,'r',encoding="utf-8")
    text=file.read()
    file.close()
    words = jieba.lcut(text)
    counts = {}
    for word in words:
        if len(word) == 1:
            continue
        else:
            counts[word] = counts.get(word,0) + 1
    items = list(counts.items())
    items.sort(key = takeSecond,reverse=True)   
    for i in range(20):
        item=items[i]
        keyWord =item[0]
        count=item[1]
        print("{0:<10}{1:>5}".format(keyWord,count))
main()

我们介绍一下突出显示的新增代码的含义。当通过 for 循环来遍历列表 words 中的每个元素时,在循环体中,增加了一个判断条件。如果表示每个词语的变量 word 的长度等于 1,则直接进入下一次循环;否则,才会统计这个词语出现的次数。

运行程序,得到的词频统计结果如图 2 所示。

Python  jieba筛选词组
图 2

现在得到的词语已经都是词组了,这个结果要比单个字的词语更有实际意义。但是,显然这样还是不够的,因为诸如“一个”“那里”和“怎么”这样的词组,对于我们理解西游记也没有什么帮助。下节,我们会介绍如何去除这类不需要的词语。