如何用Python发送电子邮件?

时间:2011-06-07 19:48:05

标签: python email function smtplib

此代码可以正常运行并向我发送电子邮件:

import smtplib
#SERVER = "localhost"

FROM = 'monty@python.com'

TO = ["jon@mycompany.com"] # must be a list

SUBJECT = "Hello!"

TEXT = "This message was sent with Python's smtplib."

# Prepare actual message

message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)

# Send the mail

server = smtplib.SMTP('myserver')
server.sendmail(FROM, TO, message)
server.quit()

但是,如果我尝试将其包装在这样的函数中:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

并调用它我得到以下错误:

 Traceback (most recent call last):
  File "C:/Python31/mailtest1.py", line 8, in <module>
    sendmail.sendMail(sender,recipients,subject,body,server)
  File "C:/Python31\sendmail.py", line 13, in sendMail
    server.sendmail(FROM, TO, message)
  File "C:\Python31\lib\smtplib.py", line 720, in sendmail
    self.rset()
  File "C:\Python31\lib\smtplib.py", line 444, in rset
    return self.docmd("rset")
  File "C:\Python31\lib\smtplib.py", line 368, in docmd
    return self.getreply()
  File "C:\Python31\lib\smtplib.py", line 345, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

任何人都可以帮我理解为什么吗?

12 个答案:

答案 0 :(得分:166)

我建议您一起使用标准软件包emailsmtplib来发送电子邮件。请查看以下示例(从Python documentation转载)。请注意,如果您遵循这种方法,“简单”任务确实很简单,并且更复杂的任务(如附加二进制对象或发送普通/ HTML多部分消息)可以非常快速地完成。

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.text import MIMEText

# Open a plain text file for reading.  For this example, assume that
# the text file contains only ASCII characters.
with open(textfile, 'rb') as fp:
    # Create a text/plain message
    msg = MIMEText(fp.read())

# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you

# Send the message via our own SMTP server, but don't include the
# envelope header.
s = smtplib.SMTP('localhost')
s.sendmail(me, [you], msg.as_string())
s.quit()

要将电子邮件发送到多个目的地,您还可以按照Python documentation

中的示例进行操作
# Import smtplib for the actual sending function
import smtplib

# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

# Create the container (outer) email message.
msg = MIMEMultipart()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'

# Assume we know that the image files are all in PNG format
for file in pngfiles:
    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    with open(file, 'rb') as fp:
        img = MIMEImage(fp.read())
    msg.attach(img)

# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.sendmail(me, family, msg.as_string())
s.quit()

如您所见,To对象中的标题MIMEText必须是由逗号分隔的电子邮件地址组成的字符串。另一方面,sendmail函数的第二个参数必须是字符串列表(每个字符串都是一个电子邮件地址)。

因此,如果您有三个电子邮件地址:person1@example.comperson2@example.comperson3@example.com,则可以执行以下操作(省略明显的部分):

to = ["person1@example.com", "person2@example.com", "person3@example.com"]
msg['To'] = ",".join(to)
s.sendmail(me, to, msg.as_string())

"","".join(to)部分从列表中生成一个字符串,以逗号分隔。

从你的问题中我得知你还没有经过the Python tutorial - 如果你想在Python中找到任何地方,这是必须的 - 文档对标准库来说非常好。

答案 1 :(得分:61)

嗯,你想得到一个最新和现代的答案。

以下是我的回答:

当我需要在python中邮寄时,我会使用mailgun API,因为发送邮件已经解决了很多令人头疼的问题。他们有一个很棒的应用程序/ api,允许您每月免费发送10,000封电子邮件。

发送电子邮件将是这样的:

def send_simple_message():
    return requests.post(
        "https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
        auth=("api", "YOUR_API_KEY"),
        data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
              "to": ["bar@example.com", "YOU@YOUR_DOMAIN_NAME"],
              "subject": "Hello",
              "text": "Testing some Mailgun awesomness!"})

您还可以跟踪事件以及更多内容,请参阅the quickstart guide

我希望你觉得这很有用!

答案 2 :(得分:35)

我想通过建议yagmail包来帮助您发送电子邮件(我是维护者,抱歉广告,但我觉得它真的有用!)。

您的整个代码将是:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)

请注意,我提供了所有参数的默认值,例如,如果您要发送给自己,则可以省略TO,如果您不想要主题,也可以省略它。

此外,目标还在于使附加HTML代码或图像(以及其他文件)变得非常容易。

您放置内容的地方可以执行以下操作:

contents = ['Body text, and here is an embedded image:', 'http://somedomain/image.png',
            'You can also find an audio file attached.', '/local/path/song.mp3']

哇,发送附件是多么容易!这将需要20行没有yagmail;)

此外,如果您设置一次,您将永远不必再次输入密码(并安全存储)。在您的情况下,您可以执行以下操作:

import yagmail
yagmail.SMTP().send(contents = contents)

更简洁!

我邀请您查看github或直接使用pip install yagmail进行安装。

答案 3 :(得分:14)

存在缩进问题。以下代码可以使用:

import textwrap

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = textwrap.dedent("""\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT))
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

答案 4 :(得分:8)

试试这个:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    import smtplib
    """this is some test documentation in the function"""
    message = """\
        From: %s
        To: %s
        Subject: %s
        %s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    "New part"
    server.starttls()
    server.login('username', 'password')
    server.sendmail(FROM, TO, message)
    server.quit()

适用于smtp.gmail.com

答案 5 :(得分:4)

在函数中缩进代码(这没关系)时,你也缩进了原始消息字符串的行。但是前导空格意味着标题行的折叠(连接),如RFC 2822 - Internet Message Format的2.2.3和3.2.3节所述:

  

每个标题字段在逻辑上是包含的单行字符   字段名称,冒号和字段正文。为了方便   但是,为了处理每行的998/78字符限制,   标题字段的字段主体部分可以分成多个   线表示;这被称为“折叠”。

sendmail来电的功能形式中,所有行都以空格开头,因此“展开”(连接)并且您尝试发送

From: monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

除了我们的想法之外,smtplib不再理解To:Subject:标题,因为这些名称只能在行的开头识别。相反,smtplib会假设一个很长的发件人电子邮件地址:

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.

这不起作用,因此您的例外情况也是如此。

解决方案很简单:只需保留之前的message字符串即可。这可以通过函数(如Zeeshan建议)或在源代码中立即完成:

import smtplib

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER):
    """this is some test documentation in the function"""
    message = """\
From: %s
To: %s
Subject: %s

%s
""" % (FROM, ", ".join(TO), SUBJECT, TEXT)
    # Send the mail
    server = smtplib.SMTP(SERVER)
    server.sendmail(FROM, TO, message)
    server.quit()

现在不会发生展开并发送

From: monty@python.com
To: jon@mycompany.com
Subject: Hello!

This message was sent with Python's smtplib.

这是有效的,你的旧代码做了什么。

请注意,我还保留了header和body之间的空行以容纳the RFC的3.5节(这是必需的),并根据Python样式指南PEP-0008将include放在函数外部(这是可选的。)

答案 6 :(得分:3)

它可能会标记您的消息。在将消息传递给sendMail之前打印出消息。

答案 7 :(得分:2)

以下是Python 3.x的示例,比2.x简单得多:

import smtplib
from email.message import EmailMessage
def send_mail(to_email, subject, message, server='smtp.example.cn',
              from_email='xx@example.com'):
    # import smtplib
    msg = EmailMessage()
    msg['Subject'] = subject
    msg['From'] = from_email
    msg['To'] = ', '.join(to_email)
    msg.set_content(message)
    print(msg)
    server = smtplib.SMTP(server)
    server.set_debuglevel(1)
    server.login(from_email, 'password')  # user & password
    server.send_message(msg)
    server.quit()
    print('successfully sent the mail.')

调用此函数:

send_mail(to_email=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')
以下

仅适用于中国用户:

如果您使用126/163,网易邮箱,则需要设置&#34;客户端授权密码&#34;,如下所示:

enter image description here

参考:https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples

答案 8 :(得分:2)

确保您已授予发件人和收件人的权限,以发送电子邮件和接收来自电子邮件帐户中未知来源(外部来源)的电子邮件。

import smtplib

#Ports 465 and 587 are intended for email client to email server communication - sending email
server = smtplib.SMTP('smtp.gmail.com', 587)

#starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS.
server.starttls()

#Next, log in to the server
server.login("#email", "#password")

msg = "Hello! This Message was sent by the help of Python"

#Send the mail
server.sendmail("#Sender", "#Reciever", msg)

enter image description here

答案 9 :(得分:0)

就你的代码而言,除了之外,似乎并没有任何根本性的错误,但目前还不清楚你是如何实际调用该函数的。我能想到的是,当您的服务器没有响应时,您将收到此SMTPServerDisconnected错误。如果您在smtplib中查找getreply()函数(摘录如下),您将会有一个想法。

def getreply(self):
    """Get a reply from the server.

    Returns a tuple consisting of:

      - server response code (e.g. '250', or such, if all goes well)
        Note: returns -1 if it can't read response code.

      - server response string corresponding to response code (multiline
        responses are converted to a single, multiline string).

    Raises SMTPServerDisconnected if end-of-file is reached.
    """

查看https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py上的示例,该示例还使用函数调用来发送电子邮件,如果这是您尝试做的事情(DRY方法)。

答案 10 :(得分:0)

以为我在这里放了两个位,因为我刚刚弄清楚它是如何工作的。

您似乎没有在SERVER连接设置上指定端口,这在我尝试连接到未使用默认端口的SMTP服务器时影响了我一点:25。

根据smtplib.SMTP文档,您的ehlo或helo请求/响应应该自动处理,因此您不必担心这一点(但如果所有其他方法都失败,可能需要确认)。

要问自己的另一件事是您是否允许SMTP服务器本身的SMTP连接?对于像GMAIL和ZOHO这样的网站,您必须实际进入并激活电子邮件帐户中的IMAP连接。你的邮件服务器可能不允许那些不是来自'localhost'的SMTP连接?要研究的东西。

最后一件事是您可能想尝试在TLS上启动连接。现在大多数服务器都需要这种类型的身份验证。

你会看到我把两个TO字段塞进我的电子邮箱中。 msg ['TO']和msg ['FROM'] msg字典项允许在电子邮件本身的标题中显示正确的信息,这些信息在收件人/发件人字段中的电子邮件接收端看到(您甚至可以在这里添加一个回复字段.TO和FROM字段本身就是服务器所需要的。我知道我听说过一些电子邮件服务器拒绝电子邮件,如果他们没有适当的电子邮件标题。

这是我在函数中使用的代码,它可以使用本地计算机和远程SMTP服务器(如图所示ZOHO)通过电子邮件发送* .txt文件的内容:

def emailResults(folder, filename):

    # body of the message
    doc = folder + filename + '.txt'
    with open(doc, 'r') as readText:
        msg = MIMEText(readText.read())

    # headers
    TO = 'to_user@domain.com'
    msg['To'] = TO
    FROM = 'from_user@domain.com'
    msg['From'] = FROM
    msg['Subject'] = 'email subject |' + filename

    # SMTP
    send = smtplib.SMTP('smtp.zoho.com', 587)
    send.starttls()
    send.login('from_user@domain.com', 'password')
    send.sendmail(FROM, TO, msg.as_string())
    send.quit()

答案 11 :(得分:0)

值得注意的是,SMTP模块支持上下文管理器,因此不需要手动调用quit(),这将确保即使有异常也总是调用它。

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.ehlo()
        server.login(user, password)
        server.sendmail(from, to, body)