如何使用Python imaplib回复邮件并附上原始消息?

18 投票
1 回答
12474 浏览
提问于 2025-04-15 18:46

我现在正在使用imaplib从服务器获取电子邮件,并处理邮件内容和附件。

我想回复这些邮件,内容包括状态/错误信息,以及指向我网站上生成内容的链接,前提是这些邮件可以被处理。回复中应该包含原始邮件的内容,但要去掉任何附件(因为附件可能很大),最好只保留附件的文件名和大小。

因为我已经在处理MIME邮件的各个部分,所以我想我需要做的是构建一个新的MIME邮件树,里面包含原始邮件的副本,并删除或替换掉附件的部分。

在我开始这个过程之前,我希望有人能给我一些建议。有没有什么库函数可以做到这一点?有没有什么标准的做法我应该遵循?

我目前知道并使用的有imaplibsmtplibemail模块,但可能在里面漏掉了一些明显的东西。这段代码也在Django中运行,所以如果使用django.core.email能更方便的话,我也可以用它。

1 个回答

22

收到的邮件的原始MIME树结构如下(使用 email.iterators._structure(msg)):

multipart/mixed
    text/html                (message)
    application/octet-stream (attachment 1)
    application/octet-stream (attachment 2)

通过GMail回复后,结构变成了这样:

multipart/alternative
    text/plain
    text/html

也就是说,他们并没有像我想的那样聪明,只是丢掉了附件(这很好),并提供了文本和HTML版本,明确重组了“引用内容”。

我开始觉得我也应该这样做,只回复一个简单的消息,因为丢掉附件后,保留原始消息也没什么意义。

不过,既然我现在已经搞清楚了,还是回答我最初的问题吧。

首先,把原始消息中的所有附件替换成文本占位符:

import email

original = email.message_from_string( ... )

for part in original.walk():
    if (part.get('Content-Disposition')
        and part.get('Content-Disposition').startswith("attachment")):

        part.set_type("text/plain")
        part.set_payload("Attachment removed: %s (%s, %d bytes)"
                         %(part.get_filename(), 
                           part.get_content_type(), 
                           len(part.get_payload(decode=True))))
        del part["Content-Disposition"]
        del part["Content-Transfer-Encoding"]

然后创建一条回复消息:

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.message import MIMEMessage

new = MIMEMultipart("mixed")
body = MIMEMultipart("alternative")
body.attach( MIMEText("reply body text", "plain") )
body.attach( MIMEText("<html>reply body text</html>", "html") )
new.attach(body)

new["Message-ID"] = email.utils.make_msgid()
new["In-Reply-To"] = original["Message-ID"]
new["References"] = original["Message-ID"]
new["Subject"] = "Re: "+original["Subject"]
new["To"] = original["Reply-To"] or original["From"]
new["From"] = "me@mysite.com"

接着把原始的MIME消息对象附上,然后发送:

new.attach( MIMEMessage(original) )

s = smtplib.SMTP()
s.sendmail("me@mysite.com", [new["To"]], new.as_string())
s.quit()

最终的结构是:

multipart/mixed
    multipart/alternative
        text/plain
        text/html
    message/rfc822
        multipart/mixed
            text/html
            text/plain
            text/plain

或者用Django会简单一些:

from django.core.mail import EmailMultiAlternatives
from email.mime.message import MIMEMessage

new = EmailMultiAlternatives("Re: "+original["Subject"],
                             "reply body text", 
                             "me@mysite.com", # from
                             [original["Reply-To"] or original["From"]], # to
                             headers = {'Reply-To': "me@mysite.com",
                                        "In-Reply-To": original["Message-ID"],
                                        "References": original["Message-ID"]})
new.attach_alternative("<html>reply body text</html>", "text/html")
new.attach( MIMEMessage(original) ) # attach original message
new.send()

结果在GMail中显示原始消息为“---- 转发的消息 ----”,这并不是我想要的效果,但大体思路是可行的,希望这个回答能帮助到那些想要处理MIME消息的人。

撰写回答