在Python中解析mbox文件

17

作为 Python 新手,我想遍历一个大的 mbox 文件并解析电子邮件消息。我可以使用以下代码实现:

import sys
import mailbox

def gen_summary(filename):
    mbox = mailbox.mbox(filename)
    for message in mbox:
       subj = message['subject']
       print subj

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print 'Usage: python genarchivesum.py mbox'
        sys.exit(1)

    gen_summary(sys.argv[1])

但是我需要更多的控制。我需要能够获取mbox文件中给定电子邮件的起始字节位置,并且还需要获取消息的字节数(在磁盘上表示)。接下来,在将来,我需要能够寻找到给定的邮件并解析它,而不是从mbox文件的开头开始迭代(因此需要获取磁盘上的字节位置之一)。这些都是大型的mbox文件,效率是一个问题。

所有这些的目的是为了生成一个摘要文件,其中包含有关mbox中每个电子邮件的一些小细节,并且在将来能够高效地查找单个电子邮件。


我从未使用过mailbox,但我刚刚阅读了help(mailbox.mbox)。您不能使用.iterkeys()方法获取键值的迭代器,然后使用键值查找消息吗?为什么要使用字节索引作为查找消息的键,而不是使用该模块...您是否尝试使用模块按键索引消息?如果您尝试过并且太慢或其他原因,请说明。 - steveha
假设我有一个包含10,000封电子邮件的mbox文件。当我只需要最后一封电子邮件时,我不想读取/解析/迭代超过9,998封电子邮件。我希望能够在mbox文件中寻找到该点并仅读取该消息。 - Mark Fletcher
@MarkFletcher 你解决了这个问题吗?如果解决了,请告诉我。 - kingmakerking
1个回答

10
我没有测试过这个,但是像这样的东西可能适合你。只需要打开文件(以二进制模式打开以确保字节计数正确),然后浏览文件,查找信息。
def is_mail_start(line):
    return line.startswith("From ")

def build_index(fname):
    with open(fname, "rb") as f:
        i = 0
        b = 0
        # find start of first message
        for line in f:
            b += len(line)
            if is_mail_start(line):
                break
        # find start of each message, and yield up (index, length) of previous message
        for line in f:
            if is_mail_start(line):
                yield (i, b)
                i += b
                b = 0
            b += len(line)
        yield (i, b) # yield up (index, length) of last message

# get index as a list
mbox_index = list(build_index(fname))

一旦您获得了索引,您可以在文件对象上使用.seek()方法来寻找它,并且可以使用文件对象上的.read(length)方法仅读取一个消息。我不确定您将如何使用mailbox模块与字符串一起使用,因为我认为它是用来在原地处理邮箱的。也许有其他邮件解析模块可供使用。


1
好的,谢谢。我想我会使用类似这样的策略。顺便说一下,在mbox中的电子邮件开头以'From '(没有冒号)开头。我可以使用email.Parser来解析电子邮件。谢谢。 - Mark Fletcher
我会编辑答案,去掉“:”。我确实说过我没有测试过它...祝你的项目好运,并度过愉快的周末! - steveha
就算是为了未来的用户,至少在最新版本的OSX上,实际上两者都是如此。 def is_mail_start(line): return line.startswith("From") and not line.startswith("From:") - adammenges
如果标记开始的“From”总是后跟一个空格,您可以搜索字符串“From ”(注意末尾的空格)。这不会匹配带有冒号的“From:”。 - steveha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接