首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >嘀~正则表达式快速上手指南(下篇)

嘀~正则表达式快速上手指南(下篇)

作者头像
AI研习社
发布2018-07-26 14:26:38
3.9K0
发布2018-07-26 14:26:38
举报
文章被收录于专栏:AI研习社AI研习社

以循环方式获取每个名称和地址

接下来我们在电子邮件的 contents 列表中工作。

上面的代码中用 for 循环去遍历 contents 这样我们就可以一个一个处理每封邮件。我们创建一个字典, emails_dict,这将保存每个电子邮件的所有细节,如发件人的地址和姓名。事实上,这些是我们要寻找的第一项信息。

这个过程总共有 3 步,首先是找到 From: 字段

第一步,我们通过 re.search() 函数找到完整的 From: 字段。 句点 . 表示除了\n之外的任何字符 ,* 延伸到该行的结尾处。然后将它赋给变量 sender.

但是,数据并不总是直截了当的。常常会有意想不到的情况出现。例如,如果没有 From: 字段怎么办?脚本将报错并中断。在步骤2中可以避免这种情况。

为了避免由 From: 域导致的错误,我们要用一个 if 来检查 sender 是不是 None。如果是一个空字段的话,用 s_email 和 s_name 的值来取代 None ,这样脚本就可以继续运行而不是意外中断。

虽然这个教程让使用正则表达式看起来很简单(Pandas在下面)但是也要求你有一定实际经验。例如,我们知道使用if-else语句来检查数据是否存在。事实上,之所以我们知道如何处理,是因为我们在写这个脚本时反复地尝试过。编写代码是一个迭代过程。值得注意的是,即使教程看起来是线性的,即使教程看起来是直截了当的,但实践中需要更多的尝试。

第二步中使用了一个之前熟悉的正则表达式 \w\S*@.*\w, 用来 匹配实际的邮件地址格式。

我们用不同的规则来命名,每一个名字的左边都用 "From:" 字段中的:来分割,电子邮件的右边用开括号 <。因此可以用 :.*< 形式来找邮件名称。 我们从每个结果中快速的去掉 : 和 <

现在,让我们打印出代码的结果来看看。

注意我们没有使用 sender 变量在 re.search()函数中作为搜索字符串。我们已经打印了 sender 和 sender.group() 的类型,这样就能看到区别。看起来 sender 是一个 re 的匹配对象,并且不能用re.search()来搜索。然而sender.group() 是一个字符串,而 re.search 接受的参数即是字符串形式。

我们来看看 s_email 和 s_name 长什么样子。

同样,我们得到了匹配的对象。每次对字符串进行re.search() 操作, 都会生成匹配对象, 我们必须将其转换为字符串对象。

在转换之前,回想一下如果没有From: 字段,,sender 的值将会是None,那么 s_email和s_name 的值也将为None。因此,我们必须再次进行检查,以便脚本不会意外中断。先看看如何针对s_email 构造代码。

在步骤3A中,我们使用了if 语句来检查s_email的值是否为 None, 否则将抛出错误并中断脚本。

然后,我们只需将s_email 匹配的对象转换为字符串并将其分配给变量sender_email 即可。将转换完的字符串添加到 emails_dict 字典中,以便后续能极其方便地转换为pandas数据结构。

在步骤3B中,我们对 s_name 进行几乎一致的操作.

就像之前做的一样,我们在步骤3B中首先检查s_name 的值是否为None 。

然后,在将字符串分配给变量前,我们调用两次了 re 模块中的re.sub() 函数。首先,通过用空字符“”代替:\s* ,删除冒号及冒号与姓名之间的任何空格字符。然后删除姓名另一侧的空格字符和角括号,再次使用空字符进行替换。最终,将字符串分配给 sender_name并添加到字典中。

让我们检查下结果。

非常棒!我们已经分离了邮箱地址和发件人姓名, 还将它们都添加到了字典中,接下来很快就能用上。

既然我们已经得到了发件人的邮箱地址和姓名,通过同样的步骤就能获得收件人的邮箱地址和姓名并保存到字典中去。

首先,我们找到To: 字段。

接下来,我们将先发制人,避免recipient 为None的情况发生。

如果 recipient 不为 None, 使用 re.search() 来查找包含发件人邮箱地址和姓名的匹配对象,否则,我们将传递None值给 r_email 和 r_name 。

然后我们将匹配对象转换为字符串并添加至字典中去。

因为From: 和 To: 字段具有相同的结构,因此我们可以对两者使用相同的代码,但对其他字段来说,我们需要定制稍微不同的代码。

获取邮件的日期

现在让我们来获取邮件的发送日期。

我们获取的Date:字段的代码与From:及To:字段的代码相同。就像保证这两个字段的值不是None一样,我们同样要检查被赋值到变量date_field的值是否为 None。

我们已经输出 date_field.group(),因此可以更清楚地看到这一字符串的结构,它包含了邮件发送当天的具体日期并以“日-月-年” 的格式呈现,同时还包含了时间,但我们只想知道日期。 得到日期的代码与得到姓名和邮件地址的代码非常相似,但更简单一些,可能这儿唯一的疑惑点是正则表达式:\d+\s\w+\s\d+。

日期是以数字开始的,因此我们可以用 \d 来解析它,就像日期格式中具体天数部分一样,它可能是由一位或者两位数字组成,所以在此+ 就变得非常重要了。在正则表达式里, 在+ 的左侧来匹配一个或多个模式实例。用\d+ 来匹配可以不用考虑日期的具体天数是一位还是两位数字。

之后的一个空格可以通过寻找空白字符的 \s 来解析。月份是由三个字母组成的,因此使用\w+ 来解析,再接另一个空格,所以继续用 \s 解析。因为年份是由多个数字组成,所以我们需要再用一次\d+ 。

表达式 \d+\s\w+\s\d+之所以能起作用,是因为精确的模式匹配约束着空格之间的内容。

接下来,我们做和之前相同的 None 值检查。

如果 date 不为 None ,我们就把它从这个匹配对象转换成一个字符串,然后赋值给变量 date_sent,再将其键值添加到字典中。

进行下一步前,我们应特别注意的是+ 和 * 看起来很相似,但是它们差异很大。用日期字符串来举例:

如果使用 * 我们将匹配到大于等于零个的结果,而 + 匹配大于等于一个的结果。参照以上示例,我们输出了两种不同的结果,它们之间存在非常大的差异。正如所见, + 可以解析出整个日期而*只解析出一个空格和数字1。

接下来讲解邮件的标题。

获得邮件的标题

我们可以像之前一样,用相同的代码架构来获取我们需要的信息。

现在我们对正则表达式的格式已经很熟悉了对吧?这个代码与之前的类似,为获得标题,我们可以用一个空的字符串来代替"Subject: " 。

获取邮件的内容

最后要添加到字典里的一项就是邮件的内容了。

将标题从邮件内容中分离出来是非常复杂的任务,尤其当文中有很多不同形式的标题。在原始混乱的数据中是很难找到一致性的规律,但是幸运的是这个工作有人帮我们解决了——Python的email 模块包非常适用这项任务。

我们之前已经导入了email模块. 现在,我们将 message_from_string()方法应用于item, 将整个email转换成 email消息对象. 一个消息对象由消息头和消息体组成, 分别对应于email的头部和主体.

接下来, 我们对email消息对象使用 get_payload()方法. 提取email内容. 并将内容传递给变量 body, 稍后我们会将其存储在字典 emails_dict 的键 "email_body"下.

在处理邮件正文时为什么选择email包而非正则表达式

你可能会疑惑, 为什么使用 email 包而不是正则表达式呢? 因为在不需要大量的清理工作时,正则表达式并不是最好的方法。我们需要为这段代码做详细解释。

我们值得探讨为何会作出这个选择。但在开始之前,我们需要先理解方括号[ ] 在正则表达式中的含义, .

[ ] 用于匹配所有被它括起来的内容. 比如, 如果需要在字符串中查找 "a", "b", 或 "c" , 可以使用 [abc] 作为模式. 上文提到过的模式也适用。[\w\s] 用于查找字母、数字或空格。不同之处在于,它匹配的是方括号中的文字部分。

现在,可以更好的理解我们为何会决定选择email模块了。

仔细留意下数据就会发现email头部采用字符串 "Status: 0" 或 "Status: R0"作为结束,并在下一封邮件的 From r 字符串前结束,我们可以使用 Status:\s*\w*\n*[\s\S]*From\sr* 来获取email内容. [\s\S]* 用来查找空格或非空格字符,所以用于大段的文本、数字,以及标点符号。

不幸的是一封 email 不止一个“Status: ” 字符串,也并不一定都包含 "From r",即邮件拆分之后的数目可能会比邮件列表的字典数目多 也可能会比它少 ,但它们不会和已有的其他类别相匹配。如果使用 pandas 包来解决这个问题的话 会遇到问题 ,因此,我们选择使用 email 包。

创建字典列表

最后,添加字典emails_dict到 emails 列表:

此时可以打印emails列表。执行 print(len(emails_dict)) 函数,查看列表中有多少字典和email 。如前述,全部语料库包含 3977个email。我们的小型测试文件中只有7个。全部代码如下:

我们已经打印出了emails 列表的第一项, 它是由键和键值对组成的字典. 由于使用了 for 循环,因此每个字典拥有相同的键,但键值不同。

我们为每个 item 赋值 "email content here" ,所以不需要打印所有的email来占据电脑屏幕. 如果你在家应用时打印email,你将会看到实际的email内容。

使用 pandas 处理数据

如果使用 pandas 库处理列表中的字典 那将非常简单。每个键会变成列名, 而键值变成行的内容。

我们需要做的就是使用如下代码:

通过上面这行代码,使用pandas的DataFrame() 函数,我们将字典组成的 emails 转换成数据帧,并赋给变量emails_df.

就这么简单。我们已经拥有了一个精致的Pandas数据帧,实际上它是一个简洁的表格,包含了从email中提取的所有信息。

请看下数据帧的前几行:

The dataframe.head() 函数显示了数据序列的前几行。该函数接受1个参数。一个可选的参数用于定义需要显示的行数, n=3 表示前3行。

也可以精确地查找。例如,查找从特定域名发来的邮件。但是,我们需要先学习一种新的正则表达式来完成精确查询工作。

管道符号, |, 用于查找位于它两边的任意字符。 如, a|b查找 a 或 b。

| 有点类似 [ ], 但二者有区别。假设我们需要查找"crab", "lobster", 或 "isopod"。 使用 crab|lobster|isopod 会比 [crablobsterisopod] 更精确,前者会匹配完整单词,而后者只匹配单个字符。

现在我们可以使用 | 符号查找从特定域名发送来的email。

这里我们使用了一行超长的代码。由内及外剖析它。

emails_df['sender_email'] 选择了标记为 sender_email的列,接下来,如果在该列中匹配到 子字符串 "maktoob" 或 "spinfinder" ,则str.contains(maktoob|spinfinder) 返回 True . 最后, 最外面的emails_df[] 返回 sender_email 列视图,该列包含需要匹配的目标字符串。干的漂亮!

我们也可以单个检视邮件。 只需要以下4步。 第1步,查找包含字符串"@maktoob"的列 "sender_email" 对应的行索引。请留意我们是如何使用正则表达式来完成这项任务的。

第2步,使用索引查找email地址, loc[] 方法返回一系列不同属性的对象. 并将其打印出来,以便查看。

第3步,从这一系列对象中提取email地址,并罗列出来,现在你会发现他的类型是now类。

第4步将展示提取到的email正文

在第四步中 emails_df['sender_email'] == "james_ngola2002@maktoob.com" 是用来查找包含 "james_ngola2002@maktoob.com" 的邮件发送者列,接下来 ['email_body'].values 用来查找邮件正文的相同行的列值,最后输出该列值。

如你所见,我们可以多种方式应用正则表达式,正则表达式也能与pandas完美配合。

其他资源

自从应用范围从生物学扩展到工程领域,过去这些年正则表达式发展速度惊人 。今天,正则表达式已可在多种变成语言中应用,除基本模式外,有适当变化。在这份教程中,我们使用Python练习使用正则表达式,但如果你喜欢,也可以使用 Stack Overflow 发掘它的其他特点。维基百科用一张表格比较了不同正则表达式引擎的特点。

正则表达式还有很多特性本教程不能一一列举,完整的文档可以参考Python文档中的 re 模块. 谷歌也有一份快速参考手册(https://developers.google.com/edu/python/regular-expressions)。

如果需要一系列数据进行实验的话, Kaggle 和 StatsModels 将对你有所帮助。

这里是正则表达式的速查表,但对大多数来说也是有帮助的。

如果这篇教程对你有用的话,你也会喜欢 Dataquest 的正则表达式课程。

原文链接:https://www.dataquest.io/blog/regular-expressions-data-scientists/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI研习社 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 以循环方式获取每个名称和地址
  • 获取邮件的日期
  • 获得邮件的标题
  • 获取邮件的内容
  • 创建字典列表
  • 使用 pandas 处理数据
  • 其他资源
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档