本章介绍一些基本的邮件(虽然社交网络有很多优点,但是在未来的几年中,邮件仍将是互联网的主打产品)分析工具和技术,以回答以下问题:
谁发送的邮件最多?
一天中是否存在某个特定的时间(或是一周中的某天),这时候发件人最可能收到对一个问题的回复?
几个人之间,谁发送的消息最多?
讨论最热烈的话题是什么?
虽然社交媒体网站积累了越来越多的准实时社交数据,但它还是存在重大的缺点。和Email 不同,社交网络数据是由服务提供者集中管理的,服务提供者可以创建规则,规定访问它的方式,以及你能做什么、不能做什么注1。另一方面,邮件数据主要是分 散管理的,它以富邮件列表的形式分散在Web 上,是对一连串有趣话题的讨论。如果你使用Google 和Yahoo! 这类服务提供商提供的服务来检索邮件列表,它们就会限制邮件列表数据的使用,但可以用稍微简单的方法来挖掘内容,这会提高成功率:可以通过订阅列
趋势。
注1:
你可能已经猜到了,要想找到作为例证的真实的社交数据集并不容易(如果还能找到的话),但幸运的是,本章有最为真实的数据集:公开的Enron 邮件数据集(http://www. cs.cmu.edu/~enron/ )。
mbox:Unix 的入门级邮箱(1)
假如你之前还没有见过mbox 文件,那么我可以告诉你,从根本上说它是大型的串联邮件消息文本,很容易由基于文本的工具获取。每封邮件的开头都是由特殊的From_line 标记的,并将它格式化为"From user@example.com Fri Dec 25 00:06:42 2009" 模式,其中日期/时间戳是asctime(http://opengroup.org/onlinepubs/007908775/xsh/asctime.html )格式,它是一种标准的固定长度的时间戳表示。
注意:mbox 格式是很常见的,大多数邮件客户端都提供了“导出”或者“另存为”选项,可以将数据导出为此格式,而不关心底层实现。
在mobox 文件中,两封邮件之间的边界是由两个新行前面的(除了第一次出现)From_ line 决定的。示例3-1 是一个虚构的mbox 的一小部分,它包含两条消息。
示例3-1 :示例mbox 文件的一小部分
![]() ![]() |
但是可以证明mbox 中至少还存在另一封邮件,在示例3-1 中,我们可以看到两封邮件。按时间先后来说,第一封邮件是由Buddy 撰写的,发送给workshop@northpole.example. org ,告诉他玩具已经装好了。mbox 中的另一封邮件是Santa 对Rudolph 的回复。示例mbox 中没有显示的是一封中间邮件,Rudolph 给Santa 转发了Buddy 的邮件,解释了他迟到的原因。虽然我们可以通过阅读这些邮件文本推断出发生的事情,但是我们也可以从Message-ID 、References 和In-Reply-To 标头中得到重要线索。这些标头非常直观,为显示线程化讨论和那类事情的算法提供了基础。稍后我们会研究在“电子邮件线程化”中使用了这些字段的一个著名算 法,但是重点是每封邮件都有唯一的邮件ID ,包含了对这封正在被回复的确切邮件是一个回复的情况的引用,而且可以在答复链中引用多封其他邮件,它们是正在进行的讨论进程的一部分。
注意:除了使用一些Python 模块来干这些没有技术含量的活之外,我们不会讨论电子邮件消息的相关细节,如多部分邮件、MIME(http://en.wikipedia.org/wiki/MIME )、7位内容传输编码等。如果你想仔细研究一下的话,毫不费力就能找到全面涵盖这些主题的极好的参考资料。
这些标头极为重要。即使是在这个简单的示例中,你也可以看到当你在解析一封邮件的正文时,事情如何变得混乱:Rudolph 的客户端使用“>”符号引用转发的内容,而Santa 的邮件客户端过去常常直接回复,没有引用任何内容,但是使用了一个人类可读的邮件标头。即使不是完全没有可能,但尝试从mailbox 数据中解析出确切的对话流仍很困难,因为所包含的二义性使它成为可能。当你想查看这些信息时,如果你感兴趣的是比研究原始数据存储更方便的技术的话,大多 数邮件客户端都能够显示比你平常看到的更多的扩展邮件标头。示例3-2 说明了示例3-1 的邮件流,图3-1 显示了Apple Mail 的示例标头。
![]() |
(点击查看大图)图3-1 :大多数邮件客户端都允许通过一个选项菜单来查看扩展标头 |
示例3-2 :示例3-1 的消息流
![]() |
幸运的是,有很多可以做的,而你不必从根本上重新实现一个邮件客户端。而且,如果
你只是想要浏览邮箱,可以简单地将它导入到邮件客户端并浏览它,对吧!虽然只是固定的套路,但也值得花一点时间来研究你的邮件客户端是否有一个“以mbox 格式导入/ 导出数据”的选项,这样你才可以使用本章的工具来对它进行交叉分析。
Python 提供了一些解析mbox 数据的基本工具,示例3-3 的脚本介绍了一种将mbox 数据转换为一系列JSON 对象的基本方法。
警告:请注意,示例3-3 的好几个地方包含了decode('utf-8', 'ignore') 函数。当使用email 或网页这些基于文本的数据时,由于特别的字符编码,很容易会遇到臭名昭著的UnicodeDecodeError ,发生了什么或者如何解决这个问题通常并不明显。简略的回答是可以在任何字符串值中运行decode() 函数,并且把它传递给第二个参数,该参数指定了在碰到UnicodeDecodeError 时应该怎么办。默认值是'strict' ,它会引发异常,但是也可以根据需要使用'ignore' 或'replace' 来代替它。
示例3-3 :将mbox 转换为更为方便的JSON 结构(mailboxes_jsonify_mbox.py)


警告:从Python 2.6.x 开始,jsonlib2 (可以通过easy_install 获得)这类基于C语言的第三方模块要比标准库模块快得多,但是Python 2.7 附带了与jsonlib2 这类模块等同的更新(http:// bugs.python.org/issue4136 )。为了便于说明,就不多介绍在相当大的JSON 结构(大约为100MB )使用标准库和jsonlib2 之间的区别了。
这段脚本解析出了Email 中最相关的信息,并构建出了可移植的JSON 对象。我们能做的还有很多,但是它解决了最常见的问题,包括解码quoted-printable(QP )文本(http:// en.wikipedia.org/wiki/Quoted-printable )和去除HTML 标签的简单机制。quopri 模块用于解决QP 格式,它是一种用于将8位内容转换为7位通道的编码注2。示例3-3 的简略的示例输出如示例3-4 所示。
示例3-4 :来自示例3-1 的示例mbox ,由示例3-3 产生的示例JSON 输出
![]() ![]() |