将邮件附件获取为Python文件对象

34 投票
3 回答
75595 浏览
提问于 2025-04-16 06:23

我有一个电子邮件的多部分消息对象,我想把这个邮件中的附件转换成Python的文件对象。这可能吗?如果可以的话,我应该查找Python中的哪个方法或类来完成这个任务呢?

3 个回答

11

其实现在推荐使用的 email.EmailMessage API(不要和旧的 email.Message API 混淆)其实很简单,可以做到:

  1. 遍历所有的消息元素,只选择附件

  2. 只遍历附件

假设你把消息存储在一个叫做 envelope 的变量里,内容是字节格式的。

解决方案一:

import email
from email.message import EmailMessage

email_message: EmailMessage = email.message_from_bytes(envelope, _class=EmailMessage)

for email_message_part in email_message.walk():
    if email_message.is_attachment():
        # Do something with your attachment

解决方案二:(更推荐这个,因为你不需要遍历消息对象的其他部分)

import email
from email.message import EmailMessage

email_message: EmailMessage = email.message_from_bytes(envelope, _class=EmailMessage)

for email_message_attachment in email_message.iter_attachments():
        # Do something with your attachment

有几点需要注意:

  1. 我们在读取字节时,明确告诉程序使用新的 EmailMessage 类,这通过 _class=EmailMessage 参数来实现。
  2. 你可以通过内置的方法从 message.Parser API 中读取你的邮件消息(也就是 envelope),来源可以是 字节对象、二进制文件对象或字符串
21

这里有一个可用的解决方案,消息来自IMAP服务器。

self.imap.select()
typ, data = self.imap.uid('SEARCH', 'ALL')
msgs = data[0].split()
print "Found {0} msgs".format(len(msgs))

for uid in msgs:
    typ, s = self.imap.uid('FETCH', uid, '(RFC822)')
    mail = email.message_from_string(s[0][1])

    print "From: {0}, Subject: {1}, Date: {2}\n".format(mail["From"], mail["Subject"], mail["Date"])

    if mail.is_multipart():
        print 'multipart'
        for part in mail.walk():
            ctype = part.get_content_type()
            if ctype in ['image/jpeg', 'image/png']:
                open(part.get_filename(), 'wb').write(part.get_payload(decode=True))
73

我不太明白你说的“电子邮件多部分消息对象”是什么意思。你是指属于 email.message.Message 类的对象吗?

如果是这个意思,那其实很简单。在一个多部分的消息中,get_payload 方法会返回一个消息部分的列表(每个部分本身也是一个 Message 对象)。你可以遍历这些部分,查看它们的属性:比如,get_content_type 方法会返回这个部分的 MIME 类型,而 get_filename 方法会返回这个部分的文件名(如果消息中指定了的话)。当你找到正确的消息部分后,可以调用 get_payload(decode=True) 来获取解码后的内容。

>>> import email
>>> msg = email.message_from_file(open('message.txt'))
>>> len(msg.get_payload())
2
>>> attachment = msg.get_payload()[1]
>>> attachment.get_content_type()
'image/png'
>>> open('attachment.png', 'wb').write(attachment.get_payload(decode=True))

如果你是通过编程从收到的电子邮件中提取附件,建议你要小心病毒和木马。特别是,你应该只提取那些你知道是安全的 MIME 类型的附件,并且最好自己选择文件名,或者至少要对 get_filename 的输出进行处理,确保安全。

撰写回答