python和正则表达式抽取文本化的信息

很多人的日常工作,都是要和大量的文本打交道的。例如学者需要阅读大量的文献材料,从中找到灵感、数据与论据;学生需要阅读很多教科书和论文,然后写自己的报告或者做幻灯;财经分析师,需要从大量的新闻报道中,找到行业的发展趋势和目标企业动态的蛛丝马迹。

但不是所有的文本处理,都那么新鲜而有趣。有一项重要但繁琐的工作,就是从大量的文本当中抽取结构化的信息。

然而,结构化的信息不一定就在那里,静候你来使用。很多时候,它蕴藏在以往生成的非结构化文本中。

你可能早已习惯,人工阅读文本信息,把关键点摘取出来,然后把它们拷贝粘贴放到一个表格中。从原理上讲,这样做无可厚非。但是实际操作里面,效率太低,而且太麻烦。

大部分人,是不愿意从事这种简单重复的枯燥工作的。一遍遍机械重复鼠标划定文本范围,⌃Ctrl+C、切换到表格文档、找准输入位置,再 ⌃Ctrl+V……这种事儿做得太多,对你的肩肘关节,甚至是身心健康,都有可能造成不利影响。

想不想尝试用一种更简单的自动化方式,替你快速完成这些烦人的操作步骤呢?读过本文后,希望你能找到答案。样例

这里,我们举一个极端简化的中文文本抽取信息例子。之所以这样做,是为了避免你在解读数据上花费太多时间。我更希望,你能够聚焦于方法,从而掌握新知。

假设一个高中班主任,高考后让班长统计一下学生们的毕业去向。班长很认真地进行了调查,然后做了如下汇报:

张华考上了北京大学
李萍进了中等技术学校
韩梅梅进了百货公司
……

为了让你对样例足够熟悉,甚至有共鸣,这里我从 1998 年版的新华字典中,「借鉴」了部分内容。够贴心吧?

现实生活中,一个班大概不会只有 3 个人,因此你可以想象这是一个长长的句子列表。但其实班主任有个隐含的意思没有表达出来,即:

我想要一张表格

所以,看到这一长串的句子,你可以想象他的表情。班长估计也很难堪:

想要表格你早说啊!

这时候,假设你是班长,怎么办?信息都在文本里面。但如果需要转换成表格,就得一个个信息点去寻找和处理。其实,对于四五十人的班级来说,手动操作也不是什么太难的事情。但是设想一下,如果你需要处理的数据量,是这个例子的十倍、百倍甚至千万倍呢?继续坚持手动处理?这不仅麻烦,而且不现实。

我们需要找到一种简单的方法,帮助我们自动抽取相应的信息。此处我们使用的方法,是正则表达式

正则

「正则表达式」这个名字,初听起来好像很玄妙。实际上,它是从英文 「regular expression」翻译过来的。如果译成白话,那就是 「有规律的表述形式」。

这,听起来,是不是就更加接地气了?但是,给你补一下 「假行家 101」课程:

说别人听得懂的话,你能唬得住谁?

约定俗成,咱们继续沿用 「正则表达式」,来称呼它好了。从创生之日起,它就给文本处理带来了高效率。但是,用它的主要人群,却不是时常跟文字打交道的作家、编辑、学者、文员,而是……程序员!

程序员写的代码,是文本;程序员处理的数据,很多也是文本格式。其中便有很多显著的规律可循。

正是靠着正则表达式这种独门秘籍,许多别人做起来,需要昏天黑地一整周的任务,程序员可以半小时搞定,然后喝着咖啡等下班。

即便到了泛人工智能的今天,正则表达式依然有许多令你意想不到的应用。例如人机对话系统。你可能看了新闻报道,总以为人机对话都是靠着知识图谱或者深度学习搞出来的。不能说这里面,没有上述炫酷技术的参与。但它们充其量,只占其中一部分,或许还只是一小部分。

生产实践里面,大量的对话规则后面,并不是让你倍感神奇深奥的神经网络,而是一堆正则表达式。你可能会担心,这样高端的应用技术,自己能掌握吗?

答案是:当然!

正则表达式,并不难学。尤其是当你把它和 Python 结合到一起,那简直就是效率神器了。我们这就来看看,正则表达式怎么帮我们识别出样例文本里面 「人名」和 「去向」信息。

试练

请你开启一个浏览器,键入这个 网址。

你会看见如下界面:

它可是一个正则表达式实验的利器。我教 INFO 5731 课程时,学生们就是在掌握了这个工具以后,迅速玩儿转了正则表达式。

这么好的工具,一定要价不菲吧?不,它是免费的。你放心大胆使用就好了。

我们首先把左侧的编程语言,从默认的 PHP ,调整为 Python。之后,把需要进行处理的文本,贴到中间空白的大文本框里面。

python和正则表达式抽取文本化的信息_第1张图片

下面我们来尝试进行 「匹配」。什么叫做匹配呢?就是你写一个表达式,电脑便拿着鸡毛当令箭,在每一行文本上,都认认真真地找有没有符合该表达式的文本段落。如有,则会高亮显示出来。

这里我们观察一下,发现每个句子里面,人员去向前面,都有一个 「了」字。好,我们就在中部上方小文本框里,把 「了」字输入进去。可以看到,三句话里面的 「了」,全都亮了。

python和正则表达式抽取文本化的信息_第2张图片

这就是你接触到的第一种匹配方式 —— 按照字符原本的意思来查找一致的内容。因为样例文本的规律性,我们可以把 「了」当成一个定位符,它后面,到句子结束位置,是 「去向」信息。咱们需要找的一半结构化信息,不就是这个 「去向」吗?

我们尝试匹配 「去向」。怎么匹配呢?这次每一行的字儿都不一样啊?没关系,正则表达式强大之处,此时就显示出来了。你可以用一个点号,也就是 .,表示任意字符。字母、数字、标点…… 甚至是中文,也能涵盖在内。

然后我们继续想想看,去向信息这里,会有几个字呢?不好说。例子里面这简单的三句话,就有 「4 个字」或者 「6 个字」两种情况。所以,我们无法指定去向信息里面字符的长度。但这也没关系,我们只需要用一个星号(*),就可以代表出现次数,从 0 到无穷大都可以匹配。当然,实际情况中,是不会真出现无穷大的。

我们在刚才输入的基础上,加上 .*,结果就成了这个样子:

python和正则表达式抽取文本化的信息_第3张图片

不错嘛!不过似乎去向信息和 「了」字儿都是一样颜色的高亮。那不就混到了一起吗?我们可不想这样。

怎么办?请你在 .* 的两侧,尝试加入一对小括号(注意,不要用中文全角符号)试试看。

python和正则表达式抽取文本化的信息_第4张图片

你会发现,这次 「了」依然用蓝色表示,而后面的去向信息,已经变成了绿色。这一对小括号,很重要,它叫做 「分组」,是提取信息的基本单位。

我们的任务已经解决了一半了,是吧?下面我们来尝试把人名一并抽取出来。我们来找人名的锚定位置。细细观察,你不难发现,每个人名的后面,都有个动词跟着。升学的同学,用的是 「考」字,而就业的同学,用的是 「进」字。

我们先尝试一下 「考」字。这里我们尝试直接把 「考」字放在 「了」字以前。但是你会发现,什么匹配结果也没有。

python和正则表达式抽取文本化的信息_第5张图片

为什么?回看数据,你会发现,人家用的原词是 「考上了」。当然这里我们可以输入 「上」字。不过你要考虑一下更为通用的情况。好比说,「考取了」怎么办?「考入了」呢?更好的方式,是继续使用我们刚才学会的 「大招」,在 「考」和 「了」之间,插入一个 .*

这时候,你的正则表达式的样子是 考.*了(.*)

python和正则表达式抽取文本化的信息_第6张图片

看,第一行的信息成功匹配了吧?但是,那后面还有两行没有匹配,怎么办?

我们依样画葫芦,就会发现,使用进.*了(.*) 就能正确匹配后两行:

python和正则表达式抽取文本化的信息_第7张图片

问题来了:匹配第一行的,匹配不了后两行,反之亦然。这不好。我们希望写的表达式,能够更通用

怎么办?我们看看正则表达式当中 「或」关系的表示。这里,我们可以把两个字符用竖线隔开,旁边用中括号括起来,代表两者任一出现,都算匹配成功。

也就是,把正则表达式,写成这样:[考|进].*了(.*)

python和正则表达式抽取文本化的信息_第8张图片

太棒了,三行的内容都已经匹配成功。这里,动词词组,和代表时态的 「了」作为中间锚定信息,我们可以放心大胆,把之前的人名信息,提取出来了。

也就是这样写:(.*)[考|进].*了(.*)

python和正则表达式抽取文本化的信息_第9张图片

注意此时,人名分组是绿色,去向分组是红色的。我们成功提取了两组信息!庆祝一下!

可是,如果你给班主任看这里的结果,估计他不会满意。

表格,我要表格!

别着急,该 Python 出场了。下面我们尝试在 Python 把数据正式提取出来。

代码

首先,读入 Python 正则表达式包:

import re

然后,我们把数据准备好。注意为了演示代码的通用性,我这里在最后加了一行文字,区别于之前的文字规律,看看我们的代码能否正确处理它:

data = """张华考上了北京大学
李萍进了中等技术学校
韩梅梅进了百货公司
他们都有光明的前途"""

然后,该写正则表达式了。你真的需要自己手动来写吗?当然不必。

强大的 regex101 网站,已经帮助我们准备好了:

python和正则表达式抽取文本化的信息_第10张图片

请你点击上图中红色圈出的按钮,网站会为你准备好一个初始代码的模板,可以匹配你需要的模式:

python和正则表达式抽取文本化的信息_第11张图片

你不需要完全照搬代码。其中有这样一句,是很重要的,拷贝过来,贴到 Colab Notebook 就好:

regex = r"(.*)[考|进].*了(.*)"

以上就是你的正则表达式,在 Python 里面应有的样子。

我们准备一个空列表,用来接收数据:

mylist = []

接着,写一个循环:

for line in data.split('\n'):
  mysearch = re.search(regex, line)
  if mysearch:
    name = mysearch.group(1)
    dest = mysearch.group(2)
    mylist.append((name, dest))

我给你解释一下这个循环里面,各条语句的含义:

  • data.split('\n') 把文本数据按行来拆分开。这样我们就可以针对每一行,来获取数据;
  • mysearch = re.search(regex, line) 这一句尝试匹配模式到该行内容;
  • if mysearch 这个判断语句,是让程序分辨一下,该行是否有我们要找的模式。例如最后一行文字,里面并没有咱们前面分析的文字模式。遇到这样的行,直接跳过;
  • name = mysearch.group(1) 是说匹配的第一组内容,也就是 regex101 网站里绿色代表的人名分组存到 name 变量里。下一句依次类推。注意 group 对应你正则表达式里面小括号出现的顺序,从 1 开始计数;
  • mylist.append((name, dest)) 把该行抽取到的信息,存入到咱们之前定义的空列表里面。

注意,如果不加 mysearch = re.search(regex, line) 这一句,程序会对每一行都尝试匹配并且抽取分组内容,那么结果就会报这样的错误:

python和正则表达式抽取文本化的信息_第12张图片

所以你看,用正则表达式抽取信息时,不能蛮干。

此时,我们查看一下 mylist 这个列表里面的内容:

mylist

结果为:

[('张华', '北京大学'), ('李萍', '中等技术学校'), ('韩梅梅', '百货公司')]

不错,一个不多,一个不少,恰好是我们需要的。

我们要把它导出成为表格。方法有很多,但是最简便顺手的,是用 Pandas 数据分析软件包:

import pandas as pd

只需要利用 pd.DataFrame 函数,我们就能把上面列表和元组(tuple)组成的一个二维结构,变成数据框。

df = pd.DataFrame(mylist)
df.columns = ['姓名', '去向']

注意,这里我们还非常细心地修改了表头。

看看你的劳动成果吧:

df

python和正则表达式抽取文本化的信息_第13张图片

有了数据框,转换成为 Excel ,就是一行代码的事情了:

df.to_excel("dest.xlsx", index=False)

进入 Files 标签页,刷新并且查看一下当前目录下的内容:

python和正则表达式抽取文本化的信息_第14张图片

这个 dest.xlsx 就是输出的结果了。下载之后我们可以用 Excel 打开查看。

python和正则表达式抽取文本化的信息_第15张图片

任务完成!你可以把结果提交给班主任,看他满意的笑容了。

小结

这篇教程里面,咱们谈了如何利用文本字符规律,借助 Python 和正则表达式,来提取结构化信息。

希望你已经掌握了以下本领:

  • 了解正则表达式的功用;
  • 用 regex101 网站尝试正则表达式匹配,并且生成初步的代码;
  • 用 Python 批量提取信息,并且根据需求导出结构化数据为指定格式。

再次强调一下,对于这么简单的样例,使用上述方法,绝对是大炮轰蚊子。然而,如果你需要处理的数据是海量的,这个方法给你节省下来的时间,会非常可观。希望你能够举一反三,在自己的工作中灵活运用它。

(.*)[考|进].*了(.*) 方括号中不需要使用 | ,因为方括号本身的作用就是任选其中的一个字符,[考|进] 会匹配 考、|、进 三个中的任意一个。

你可能感兴趣的:(python)