Python那些事——如何用Python抽取中文关键词?这里有全套!

环境
python
第一步是安装python运行环境。我们使用集成环境anaconda。
请到这个网址 下载最新版的anaconda。下拉页面,找到下载位置。根据你目前使用的系统,网站会自动推荐给你适合的版本下载。我使用的是macos,下载文件格式为pkg。
image
下载页面区左侧是python 3.6版,右侧是2.7版。请选择2.7版本。
双击下载后的pkg文件,根据中文提示一步步安装即可。
image
我们需要安装中文文本处理的软件包。这里咱们使用的是结巴分词。
样例
我专门为你准备了一个github项目,存放本文的配套源代码和数据。请从这个地址下载压缩包文件,然后解压。
解压后的目录名称为demo-keyword-extraction-master,样例目录包含以下内容:
image
这里面除了readme.md这个github项目默认说明文件外,还有两个文件,分别是数据文件sample.txt和程序源代码文件demo-extract-keyword.ipynb。
结巴分词
我们使用的关键词提取工具为结巴分词。
之前在《如何用python做中文分词?》一文中,我们曾经使用过该工具为中文语句做分词。这次我们使用的,是它的另一项功能,即关键词提取。
请进入终端,使用cd命令进入解压后的文件夹demo-keyword-extraction-master,输入以下命令:
pip install jieba
好了,软件包工具也已经准备就绪。下面我们执行
jupyter notebook
进入到jupyter笔记本环境。
image
环境已经准备好了,我们下面来介绍使用的中文文本数据。
数据
一开始,我还曾为寻找现成的中文文本发愁。
网上可以找到的中文文本浩如烟海。
但是拿来做演示,是否会有版权问题,我就不确定了。万一把哪位大家之作拿来做了分析,人家可能就要过问一句“这电子版你是从哪里搞到的啊?”
万一再因此提出诉讼,我可无法招架。
后来发现,我这简直就是自寻烦恼——找别人的文本干什么?用我自己的不就好了?
这一年多以来,我写的文章已有90多篇,总字数已经超过了27万。
image
我特意从中找了一篇非技术性的,以避免抽取出的关键词全都是python命令。
我选取的,是去年的那篇《网约车司机二三事》。
image
这篇文章,讲的都是些比较有趣的小故事。
我从网页上摘取文字,存储到sample.txt中。
注意,这里是很容易踩坑的地方。
因为汉字有编码问题。不同系统都有不同的默认编码,不同版本的python接受的编码也不同。你从网上下载的文本文件,也可能与你系统的编码不统一。
不论如何,这些因素都有可能导致,你打开后的文本里,到处都是看不懂的乱码。
正确的方式,是你在jupyter notebook里面,新建一个文本文件。
image
然后,会出现以下的空白文件。
image
把你从别处下载的文本,用任意一种能正常显示的编辑器打开,然后拷贝全部内容,粘贴到这个空白文本文件中,就能避免编码错乱。
好了,知道了这个窍门,下面你就能愉快地进行关键词抽取了。
执行
回到jupyter notebook的主界面,点击demo-extract-keyword.ipynb
image
对,你没看错。只需要这短短的4个语句,就能完成两种不同方式(tf-idf与textrank)的关键词抽取。
本部分我们先讲解执行步骤。不同关键词抽取方法的原理,我们放在后面介绍。
首先我们从结巴分词的分析工具箱里导入所有的关键词抽取功能。
在对应的语句上,按下shift+enter组合按键,就可以执行语句,获取结果了。
from jieba.analyse import *
然后,让python打开我们的样例文本文件,并且读入其中的全部内容到data变量。
with open('sample.txt') as f: data = f.read()
使用tf-idf方式抽取关键词和权重,并且依次显示出来。如果你不做特殊指定的话,默认显示数量为20个关键词。
for keyword, weight in extract_tags(data, withweight=true): print('%s %s' % (keyword, weight))
显示内容之前,会有一些提示,不要管它。
building prefix dict from the default dictionary ...loading model from cache /var/folders/8s/k8yr4zy52q1dh107gjx280mw0000gn/t/jieba.cacheloading model cost 0.547 seconds.prefix dict has been built succesfully.
然后列表就出来了:
优步 0.280875594782司机 0.119951947597乘客 0.105486129485师傅 0.0958888107815张师傅 0.0838162334963目的地 0.0753618512886网约车 0.0702188986954姐姐 0.0683412127766自己 0.0672533110661上车 0.0623276916308活儿 0.0600134354214天津 0.056915805679210 0.0526641740216开优步 0.0526641740216事儿 0.048554456767李师傅 0.0485035501943天津人 0.0482653686026绕路 0.0478244723097出租车 0.0448480260748时候 0.0440840298591
我看了一下,觉得关键词抽取还是比较靠谱的。当然,其中也混入了个数字10,好在无伤大雅。
下面我们尝试另一种关键词抽取方式——textrank。
for keyword, weight in textrank(data, withweight=true): print('%s %s' % (keyword, weight))
关键词抽取结果如下:
优步 1.0司机 0.749405996648乘客 0.594284506457姐姐 0.485458741991天津 0.451113490366目的地 0.429410027466时候 0.418083863303作者 0.416903838153没有 0.357764515052活儿 0.291371566494上车 0.277010013884绕路 0.274608592084转载 0.271932903186出来 0.242580745393出租 0.238639889991事儿 0.228700322713单数 0.213450680366出租车 0.212049665481拉门 0.205816713637跟着 0.20513470986
注意这次抽取的结果,与tf-idf的结果有区别。至少,那个很突兀的“10”不见了。
但是,这是不是意味着textrank方法一定优于tf-idf呢?
这个问题,留作思考题,希望在你认真阅读了后面的原理之后,能够独立做出解答。
如果你只需要应用,不需要了解原理,那么请跳过原理部分,直接看讨论吧。
原理
我们简要讲解一下,前文出现的2种不同关键词抽取方式——tf-idf和textrank的基本原理。
为了不让大家感到枯燥,这里咱们就不使用数学公式了。后文我会给出相关的资料链接。如果你对细节感兴趣,欢迎按图索骥,查阅学习。
先说tf-idf。
它的全称是 term frequency - inverse document frequency。中间有个连字符,左右两侧各是一部分,共同结合起来,决定某个词的重要程度。
第一部份,就是词频(term frequency),即某个词语出现的频率。
我们常说“重要的事说三遍”。
同样的道理,某个词语出现的次数多,也就说明这个词语重要性可能会很高。
但是,这只是可能性,并不绝对。
例如现代汉语中的许多虚词——“的,地,得”,古汉语中的许多句尾词“之、乎、者、也、兮”,这些词在文中可能出现许多次,但是它们显然不是关键词。
这就是为什么我们在判断关键词的时候,需要第二部分(idf)配合。
逆文档频率(inverse document frequency)首先计算某个词在各文档中出现的频率。假设一共有10篇文档,其中某个词a在其中10篇文章中都出先过,另一个词b只在其中3篇文中出现。请问哪一个词更关键?
给你一分钟思考一下,然后继续读。
公布答案时间到。
答案是b更关键。
a可能就是虚词,或者全部文档共享的主题词。而b只在3篇文档中出现,因此很有可能是个关键词。
逆文档频率就是把这种文档频率取倒数。这样第一部份和第二部份都是越高越好。二者都高,就很有可能是关键词了。
tf-idf讲完了,下面我们说说textrank。
相对于tf-idf,textrank要显得更加复杂一些。它不是简单做加减乘除运算,而是基于图的计算。
下图是原始文献中的示例图。
image
textrank首先会抽取词汇,形成节点;然后依据词汇的关联,建立链接。
依照连接节点的多少,给每个节点赋予一个初始的权重数值。
然后就开始迭代。
根据某个词所连接所有词汇的权重,重新计算该词汇的权重,然后把重新计算的权重传递下去。直到这种变化达到均衡态,权重数值不再发生改变。这与google的网页排名算法pagerank,在思想上是一致的。
image
根据最后的权重值,取其中排列靠前的词汇,作为关键词抽取结果。