2009-12-27 140 views
19

我想编写一个使用Python的smtplib发送电子邮件的程序。我搜索了文档和RFC,但找不到与附件相关的任何内容。因此,我确信有一些更高层次的概念我错过了。有人可以告诉我附件如何在SMTP中工作吗?如何使用SMTP发送附件?

+3

只要是明确的,有什么都在SMTP来处理这个问题,它是完全通过构造文件被发送的MIME文件处理。关于维基百科MIME的文章似乎涵盖了基本的很好。 – jcoder 2009-12-27 14:45:01

+3

直接链接到Python文档的“电子邮件示例”部分将使任何答案完整:http://docs.python.org/library/email-examples.html – 2009-12-27 15:24:29

回答

10

你想要检查的是email模块。它允许您构建MIME兼容消息,然后通过smtplib发送消息。

+1

感谢您的编辑,你有点快比我;-) – 2009-12-27 14:42:35

3

那么,附件不会以任何特殊方式处理,它们只是Message对象树的叶子。您可以在email python软件包的文档this部分中找到有关MIME兼容的任意问题的答案。

通常,任何类型的附件(读取:原始二进制数据)都可以使用base64 (或类似的)Content-Transfer-Encoding来表示。

18

下面是我从我们所做的工作应用程序中截取的一个示例。它创建一个带有Excel附件的HTML电子邮件。

import smtplib,email,email.encoders,email.mime.text,email.mime.base 

    smtpserver = 'localhost' 
    to = ['[email protected]'] 
    fromAddr = '[email protected]' 
    subject = "my subject" 

    # create html email 
    html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' 
    html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">' 
    html +='<body style="font-size:12px;font-family:Verdana"><p>...</p>' 
    html += "</body></html>" 
    emailMsg = email.MIMEMultipart.MIMEMultipart('alternative') 
    emailMsg['Subject'] = subject 
    emailMsg['From'] = fromAddr 
    emailMsg['To'] = ', '.join(to) 
    emailMsg['Cc'] = ", ".join(cc) 
    emailMsg.attach(email.mime.text.MIMEText(html,'html')) 

    # now attach the file 
    fileMsg = email.mime.base.MIMEBase('application','vnd.ms-excel') 
    fileMsg.set_payload(file('exelFile.xls').read()) 
    email.encoders.encode_base64(fileMsg) 
    fileMsg.add_header('Content-Disposition','attachment;filename=anExcelFile.xls') 
    emailMsg.attach(fileMsg) 

    # send email 
    server = smtplib.SMTP(smtpserver) 
    server.sendmail(fromAddr,to,emailMsg.as_string()) 
    server.quit() 
+6

多部分子类型应该是'混合'而不是'替代',否则在一些电子邮件客户端中不会看到附加文件。 – rhyek 2012-04-07 15:05:14

3

下面是如何发送zip文件附件和utf-8编码主题+正文的电子邮件。

由于缺乏针对此特定情况的文档和样本,因此不能直接说明这一点。

replyto中的非ASCII字符需要使用ISO-8859-1进行编码。可能存在可以做到这一点的功能。

提示:
给自己发一封电子邮件,保存它并检查内容,找出如何在Python中做同样的事情。

下面的代码,用于Python 3:

#!/usr/bin/env python3 
# -*- coding: utf-8 -*- 
# vim:set ts=4 sw=4 et: 

from os.path import basename 
from smtplib import SMTP 
from email.mime.text import MIMEText 
from email.mime.base import MIMEBase 
from email.mime.multipart import MIMEMultipart 
from email.header import Header 
from email.utils import parseaddr, formataddr 
from base64 import encodebytes 

def send_email(recipients=["[email protected]"], 
     subject="Test subject æøå", 
     body="Test body æøå", 
     zipfiles=[], 
     server="smtp.somewhere.xyz", 
     username="bob", 
     password="password123", 
     sender="Bob <[email protected]>", 
     replyto="=?ISO-8859-1?Q?M=F8=F8=F8?= <[email protected]>"): #: bool 
    """Sends an e-mail""" 
    to = ",".join(recipients) 
    charset = "utf-8" 
    # Testing if body can be encoded with the charset 
    try: 
     body.encode(charset) 
    except UnicodeEncodeError: 
     print("Could not encode " + body + " as " + charset + ".") 
     return False 

    # Split real name (which is optional) and email address parts 
    sender_name, sender_addr = parseaddr(sender) 
    replyto_name, replyto_addr = parseaddr(replyto) 

    sender_name = str(Header(sender_name, charset)) 
    replyto_name = str(Header(replyto_name, charset)) 

    # Create the message ('plain' stands for Content-Type: text/plain) 
    try: 
     msgtext = MIMEText(body.encode(charset), 'plain', charset) 
    except TypeError: 
     print("MIMEText fail") 
     return False 

    msg = MIMEMultipart() 

    msg['From'] = formataddr((sender_name, sender_addr)) 
    msg['To'] = to #formataddr((recipient_name, recipient_addr)) 
    msg['Reply-to'] = formataddr((replyto_name, replyto_addr)) 
    msg['Subject'] = Header(subject, charset) 

    msg.attach(msgtext) 

    for zipfile in zipfiles: 
     part = MIMEBase('application', "zip") 
     b = open(zipfile, "rb").read() 
     # Convert from bytes to a base64-encoded ascii string 
     bs = encodebytes(b).decode() 
     # Add the ascii-string to the payload 
     part.set_payload(bs) 
     # Tell the e-mail client that we're using base 64 
     part.add_header('Content-Transfer-Encoding', 'base64') 
     part.add_header('Content-Disposition', 'attachment; filename="%s"' % 
         os.path.basename(zipfile)) 
     msg.attach(part) 

    s = SMTP() 
    try: 
     s.connect(server) 
    except: 
     print("Could not connect to smtp server: " + server) 
     return False 

    if username: 
     s.login(username, password) 
    print("Sending the e-mail") 
    s.sendmail(sender, recipients, msg.as_string()) 
    s.quit() 
    return True 

def main(): 
    send_email() 

if __name__ == "__main__": 
    main() 
28

这里是PDF附件,文本“身体”和通过Gmail发送消息的一个例子。

# Import smtplib for the actual sending function 
import smtplib 

# For guessing MIME type 
import mimetypes 

# Import the email modules we'll need 
import email 
import email.mime.application 

# Create a text/plain message 
msg = email.mime.Multipart.MIMEMultipart() 
msg['Subject'] = 'Greetings' 
msg['From'] = '[email protected]' 
msg['To'] = '[email protected]' 

# The main body is just another attachment 
body = email.mime.Text.MIMEText("""Hello, how are you? I am fine. 
This is a rather nice letter, don't you think?""") 
msg.attach(body) 

# PDF attachment 
filename='simple-table.pdf' 
fp=open(filename,'rb') 
att = email.mime.application.MIMEApplication(fp.read(),_subtype="pdf") 
fp.close() 
att.add_header('Content-Disposition','attachment',filename=filename) 
msg.attach(att) 

# send via Gmail server 
# NOTE: my ISP, Centurylink, seems to be automatically rewriting 
# port 25 packets to be port 587 and it is trashing port 587 packets. 
# So, I use the default port 25, but I authenticate. 
s = smtplib.SMTP('smtp.gmail.com') 
s.starttls() 
s.login('[email protected]','xyzpassword') 
s.sendmail('[email protected]',['[email protected]'], msg.as_string()) 
s.quit() 
+2

这解决了我发送电子邮件的Excel文件的问题,这真是太棒了,因为它让我远离了调用Ruby的os.system!谢谢凯文! – Benjooster 2013-08-16 18:38:05

+0

使用Python xlwt模块创建.xls文件后,该解决方案也适用于我。我使用了公司的邮件服务器,而不是通过Gmail发送邮件。谢谢,和+1 – 2013-12-27 19:34:58

+0

这种方法实际上工作,是更干净的IMO! – 2014-11-16 19:34:47

1
# -*- coding: utf-8 -*- 

""" 
Mail sender 
""" 

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

import smtplib 
import pystache 
import codecs 
import time 

import sys 
reload(sys) 
sys.setdefaultencoding('utf-8') 


HOST = 'smtp.exmail.qq.com' 
PORT = 587 
USER = '[email protected]' 
PASS = 'yourpass' 
FROM = '[email protected]' 

SUBJECT = 'subject' 
HTML_NAME = 'tpl.html' 
CSV_NAME = 'list.txt' 
FAILED_LIST = [] 


def send(mail_receiver, mail_to): 
    # text = mail_text 
    html = render(mail_receiver) 

    # msg = MIMEMultipart('alternative') 
    msg = MIMEMultipart('mixed') 
    msg['From'] = FROM 
    msg['To'] = mail_to.encode() 
    msg['Subject'] = SUBJECT.encode() 

    # msg.attach(MIMEText(text, 'plain', 'utf-8')) 
    msg.attach(MIMEText(html, 'html', 'utf-8')) 

    try: 
     _sender = smtplib.SMTP(
      HOST, 
      PORT 
     ) 
     _sender.starttls() 
     _sender.login(USER, PASS) 
     _sender.sendmail(FROM, mail_to, msg.as_string()) 
     _sender.quit() 
     print "Success" 
    except smtplib.SMTPException, e: 
     print e 
     FAILED_LIST.append(mail_receiver + ',' + mail_to) 


def render(name): 
    _tpl = codecs.open(
     './html/' + HTML_NAME, 
     'r', 
     'utf-8' 
    ) 
    _html_string = _tpl.read() 
    return pystache.render(_html_string, { 
     'receiver': name 
    }) 


def main(): 
    ls = open('./csv/' + CSV_NAME, 'r') 
    mail_list = ls.read().split('\r') 

    for _receiver in mail_list: 
     _tmp = _receiver.split(',') 
     print 'Mail: ' + _tmp[0] + ',' + _tmp[1] 
     time.sleep(20) 
     send(_tmp[0], _tmp[1]) 

    print FAILED_LIST 


main()