这段代码工作,并向我发送电子邮件就好:

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

有人能告诉我为什么吗?


当前回答

在缩进函数中的代码时(这是可以的),还缩进了原始消息字符串的行。但是前导空白意味着标题行的折叠(连接),如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.

这将不起作用,因此出现异常。

解决方案很简单:只保留原来的消息字符串。这可以通过一个函数来完成(正如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.

这就是您的旧代码所做的工作。

请注意,我还保留了标题和正文之间的空行,以适应RFC的第3.5节(这是必需的),并根据Python风格指南PEP-0008(这是可选的)将include放在函数之外。

其他回答

我想通过建议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']

哇,发送附件是多么简单啊!如果没有yagmail,这大概需要20行;)

此外,如果你设置了一次,你就永远不必再输入密码(并安全地保存密码)。在你的情况下,你可以这样做:

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

这样更简洁!

我建议你看看github,或者直接用pip install yagmail安装它。

有压痕问题。下面的代码将工作:

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()

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

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)

当我需要在Python中发送邮件时,我使用mailgun API,它在发送邮件时遇到了很多麻烦。他们有一个很棒的应用程序/api,可以让你每月发送5000封免费电子邮件。

发送电子邮件是这样的:

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!"})

您还可以跟踪事件和更多信息,参见快速入门指南。

我编写了一个简单的函数send_email(),用于使用smtplib和电子邮件包发送电子邮件(链接到我的文章)。它还使用dotenv包来加载发件人的电子邮件和密码(请不要在代码中保密!)我正在使用Gmail电子邮件服务。密码是应用程序密码(这里是谷歌文档如何生成应用程序密码)。

import os
import smtplib
from email.message import EmailMessage
from dotenv import load_dotenv
_ = load_dotenv()


def send_email(to, subject, message):
    try:
        email_address = os.environ.get("EMAIL_ADDRESS")
        email_password = os.environ.get("EMAIL_PASSWORD")

        if email_address is None or email_password is None:
            # no email address or password
            # something is not configured properly
            print("Did you set email address and password correctly?")
            return False

        # create email
        msg = EmailMessage()
        msg['Subject'] = subject
        msg['From'] = email_address
        msg['To'] = to
        msg.set_content(message)

        # send email
        with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp:
            smtp.login(email_address, email_password)
            smtp.send_message(msg)
        return True
    except Exception as e:
        print("Problem during send email")
        print(str(e))
    return False

以上方法对于简单的电子邮件发送是可行的。如果您正在寻找更高级的功能,例如HTML内容或附件,当然可以手工编码,但我建议使用现有的包,例如yagmail。

Gmail每天限制500封邮件。对于每天发送许多电子邮件,请考虑事务性电子邮件服务提供商,如Amazon SES, MailGun, MailJet或SendGrid。