如何用Python发送电子邮件?

clo*_*311 155 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()
Run Code Online (Sandbox Code Playgroud)

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

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()
Run Code Online (Sandbox Code Playgroud)

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

 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
Run Code Online (Sandbox Code Playgroud)

谁能帮我理解为什么?

Esc*_*alo 177

我建议您使用标准软件包emailsmtplib一起发送电子邮件.请查看以下示例(从Python文档中再现).请注意,如果您遵循此方法,"简单"任务确实很简单,并且更复杂的任务(如附加二进制对象或发送普通/ 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()
Run Code Online (Sandbox Code Playgroud)

要将电子邮件发送到多个目标,您还可以按照Python文档中的示例进行操作:

# 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()
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,头部ToMIMEText对象必须是由用逗号分隔的电子邮件地址的字符串.另一方面,sendmail函数的第二个参数必须是字符串列表(每个字符串都是一个电子邮件地址).

因此,如果您有三个电子邮件地址:person1@example.com,person2@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())
Run Code Online (Sandbox Code Playgroud)

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

从你的问题我收集到你没有经过Python教程 - 如果你想在Python中的任何地方获取它是必须的 - 文档对于标准库来说非常好.

  • 另请注意,To:标头与信封收件人的语义不同.例如,您可以使用"Tony Meyer"<tony.meyer@gmail.com>'作为To:标题中的地址,但信封收件人必须只是"tony.meyer@gmail.com".要构建一个'nice'To:地址,请使用email.utils.formataddr,例如email.utils.formataddr("Tony Meyer","tony.meyer@gmail.com"). (3认同)
  • 小改进:文件应该使用 `with`: `with open(textfile, 'rb') as fp:` 打开。可以删除显式关闭,因为即使其中发生错误,`with` 块也会关闭文件。 (2认同)

Den*_*ene 63

那么,你想要一个最新和现代的答案.

这是我的答案:

当我需要在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!"})
Run Code Online (Sandbox Code Playgroud)

您还可以跟踪事件和更多内容,请参阅快速入门指南.

希望这个对你有帮助!

  • @PascalvKooten绝对有趣地跟随你对yagmail的不断广告(是的,先生,我下次会考虑,先生;).但我觉得很困惑,几乎没有人关心OP问题,而是提出了许多不同的解决方案.就好像我在询问如何更换2009智能灯泡一样,答案是:买一辆真正的梅赛德斯...... (6认同)
  • @flaschbier没人关心OP问题的原因是标题错误。“如何使用Python发送电子邮件?” 是人们点击该问题时会看到的实际原因,他们期望[yagmail](https://github.com/kootenpv/yagmail)可以提供的答案:简短。妳去 更多yagmail广告。 (4认同)
  • 这确实更像是"这个日期".虽然它使用外部API.我个人使用类似[yagmail](https://github.com/kootenpv/yagmail)的内容来保持内部. (3认同)

Pas*_*ten 38

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

你的整个代码是:

import yagmail
yag = yagmail.SMTP(FROM, 'pass')
yag.send(TO, SUBJECT, TEXT)
Run Code Online (Sandbox Code Playgroud)

请注意,我提供了所有参数的默认值,例如,如果您想发送给自己,您可以省略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']
Run Code Online (Sandbox Code Playgroud)

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

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

import yagmail
yagmail.SMTP().send(contents = contents)
Run Code Online (Sandbox Code Playgroud)

这更简洁!

我邀请您查看github或直接安装它pip install yagmail.

  • 这应该是最重要的解决方案. (4认同)
  • 我可以使用 gmail 以外的“yagmail”吗?我正在尝试使用我自己的 SMTP 服务器。 (3认同)
  • 这仅适用于 gmail 用户,也就是说,这对于几乎任何专业用途来说都是毫无用处的。 (2认同)

小智 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()
Run Code Online (Sandbox Code Playgroud)

  • @geotheory传递给函数的`SERVER`变量是用户凭证. (3认同)
  • 不明白如果没有用户凭据,这应该如何工作.. (2认同)
  • 不建议使用简单的字符串插值手动拼凑有效的 SMTP 消息,并且您的答案中有一个错误完美地说明了这一点。(正文需要以空行开头才能成为有效消息。)对于 20 世纪 80 年代纯文本 7 位英语 US-ASCII(附件、国际化和其他 MIME 支持)中涉及纯文本以外的任何内容(附件、国际化和其他 MIME 支持),您可以使用**真的**想要使用一个处理这些东西的库。Python `email` 库在这方面做得相当好(特别是从 3.6 开始),尽管它仍然需要对你正在做的事情有一定的了解。 (2认同)

Bel*_*ter 11

这是一个关于 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.')
Run Code Online (Sandbox Code Playgroud)

调用这个函数:

send_mail(to_email=['12345@qq.com', '12345@126.com'],
          subject='hello', message='Your analysis has done!')
Run Code Online (Sandbox Code Playgroud)

以下可能仅适用于中国用户:

如果你使用126/163,????,你需要设置“???????”,如下图:

在此处输入图片说明

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

  • 未来的读者请注意:还有一个“SMTP_SSL”类,并且可能需要设置一个“port”关键字参数。如果服务器需要 SSL,未加密版本就会挂起。 (2认同)

The*_*ser 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()
Run Code Online (Sandbox Code Playgroud)

它适用于smtp.gmail.com

  • 电子邮件发送正常,但最终乱码,没有消息或主题.好的个人资料名称顺便说一句 (2认同)

小智 7

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

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)
Run Code Online (Sandbox Code Playgroud)


fla*_*ier 5

在函数中缩进您的代码(这没问题)时,您还缩进了原始消息字符串的行。但是前导空格意味着标题行的折叠(串联),如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.
Run Code Online (Sandbox Code Playgroud)

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

monty@python.com    To: jon@mycompany.com    Subject: Hello!    This message was sent with Python's smtplib.
Run Code Online (Sandbox Code Playgroud)

这行不通,所以出现了您的异常。

解决方案很简单:只需保留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()
Run Code Online (Sandbox Code Playgroud)

现在展开不会发生,你发送

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()
Run Code Online (Sandbox Code Playgroud)

这是什么工作以及您的旧代码完成了什么。

请注意,我还保留了 headers 和 body 之间的空行以适应RFC 的第 3.5 节(这是必需的),并根据 Python 样式指南PEP-0008(这是可选的)将包含放在函数之外。

  • 注意:这篇文章的价值之一是对 SMTP 协议如何工作的细致解释。 (2认同)