如何使用Python发送电子邮件?

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

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

有人能告诉我为什么吗?

468937 次浏览

它可能会在你的信息中添加标签。在你把它传递给sendMail之前打印出消息。

我建议你一起使用标准包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()

要将电子邮件发送到多个目的地,你也可以遵循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()

如你所见,MIMEText对象中的头To必须是一个由逗号分隔的电子邮件地址组成的字符串。另一方面,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)部分从列表中生成一个单独的字符串,用逗号分隔。

从你的问题中,我收集到你没有通过Python教程 -如果你想在Python中获得任何东西,这是一个必须的-文档对于标准库来说是非常优秀的。

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

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

我想通过建议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安装它。

在缩进函数中的代码时(这是可以的),还缩进了原始消息字符串的行。但是前导空白意味着标题行的折叠(连接),如rfc2822 - Internet消息格式的2.2.3和3.2.3节所述:

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

sendmail调用的函数形式中,所有行都以空格开头,因此“未折叠”。(concatated)并且您正在尝试发送

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.

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

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

当我需要在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!"})

你也可以跟踪事件等等,参见快速入门指南

就你的代码而言,它似乎没有任何根本性的错误,除了,不清楚你实际上是如何调用这个函数的。我能想到的是,当您的服务器没有响应时,您将得到这个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方法)。

我想我应该把我的两个比特放在这里,因为我刚刚明白了它是如何工作的。

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

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

另一个问题是你是否允许在你的SMTP服务器上进行SMTP连接?对于像GMAIL和ZOHO这样的网站,你必须实际进入并激活电子邮件帐户中的IMAP连接。您的邮件服务器可能不允许SMTP连接不是来自'localhost'也许?一些值得调查的事情。

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

您将看到我在电子邮件中插入了两个TO字段。msg['TO']和msg['FROM'] msg字典项允许正确的信息显示在电子邮件本身的标题中,这可以在电子邮件的接收端的TO / FROM字段中看到(你甚至可以在这里添加一个Reply TO字段)。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()

下面是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,网易邮箱,你需要设置“客户端授权密码”,如下所示:

enter image description here

Ref: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples < / p >

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

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

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

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


port = 587  # For starttls
smtp_server = "smtp.office365.com"
sender_email = "170111018@student.mit.edu.tr"
receiver_email = "professordave@hotmail.com"
password = "12345678"
message = """\
Subject: Final exam


Teacher when is the final exam?"""


def SendMailf():
context = ssl.create_default_context()
with smtplib.SMTP(smtp_server, port) as server:
server.ehlo()  # Can be omitted
server.starttls(context=context)
server.ehlo()  # Can be omitted
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, message)
print("mail send")

使用gmail的另一个实现让我们说:

import smtplib


def send_email(email_address: str, subject: str, body: str):
"""
send_email sends an email to the email address specified in the
argument.


Parameters
----------
email_address: email address of the recipient
subject: subject of the email
body: body of the email
"""


server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login("email_address", "password")
server.sendmail("email_address", email_address,
"Subject: {}\n\n{}".format(subject, body))
server.quit()

我对发送电子邮件的包选项不满意,我决定制作并开源我自己的电子邮件发送器。它易于使用,并支持高级用例。

如何安装:

pip install redmail

用法:

from redmail import EmailSender
email = EmailSender(
host="<SMTP HOST ADDRESS>",
port=<PORT NUMBER>,
)


email.send(
sender="me@example.com",
receivers=["you@example.com"],
subject="An example email",
text="Hi, this is text body.",
html="<h1>Hi,</h1><p>this is HTML body</p>"
)

如果您的服务器需要用户和密码,只需将user_namepassword传递给EmailSender

我在send方法中包含了很多特性:

  • 包含附件
  • 将图像直接包含到HTML主体中
  • 金贾的模板
  • 漂亮的HTML表格开箱即用
< p >文档: https://red-mail.readthedocs.io/en/latest/ < / p >

源代码:https://github.com/Miksus/red-mail

经过大量的摆弄的例子,例如这里 这现在为我工作:

import smtplib
from email.mime.text import MIMEText


# SMTP sendmail server mail relay
host = 'mail.server.com'
port = 587 # starttls not SSL 465 e.g gmail, port 25 blocked by most ISPs & AWS
sender_email = 'name@server.com'
recipient_email = 'name@domain.com'
password = 'YourSMTPServerAuthenticationPass'
subject = "Server - "
body = "Message from server"


def sendemail(host, port, sender_email, recipient_email, password, subject, body):
try:
p1 = f'<p><HR><BR>{recipient_email}<BR>'
p2 = f'<h2><font color="green">{subject}</font></h2>'
p3 = f'<p>{body}'
p4 = f'<p>Kind Regards,<BR><BR>{sender_email}<BR><HR>'
        

message = MIMEText((p1+p2+p3+p4), 'html')
# servers may not accept non RFC 5321 / RFC 5322 / compliant TXT & HTML typos


message['From'] = f'Sender Name <{sender_email}>'
message['To'] = f'Receiver Name <{recipient_email}>'
message['Cc'] = f'Receiver2 Name <>'
message['Subject'] = f'{subject}'
msg = message.as_string()


server = smtplib.SMTP(host, port)
print("Connection Status: Connected")
server.set_debuglevel(1)
server.ehlo()
server.starttls()
server.ehlo()
server.login(sender_email, password)
print("Connection Status: Logged in")
server.sendmail(sender_email, recipient_email, msg)
print("Status: Email as HTML successfully sent")


except Exception as e:
print(e)
print("Error: unable to send email")


# Run
sendemail(host, port, sender_email, recipient_email, password, subject, body)
print("Status: Exit")
import smtplib


s = smtplib.SMTP(your smtp server, smtp port) #SMTP session


message = "Hii!!!"


s.sendmail("sender", "Receiver", message) # sending the mail


s.quit() # terminating the session

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

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。

只是为了补充答案,以便您的邮件传递系统可以扩展。

我建议有一个配置文件(可以是.json, .yml, .ini等),包含发件人的电子邮件配置,密码和收件人。

通过这种方式,您可以根据需要创建不同的可定制项目。

下面是一个包含3个文件,config, functions和main的小示例。纯文本邮件。

config_email.ini

[email_1]
sender = test@test.com
password = XXXXXXXXXXX
recipients= ["email_2@test.com", "email_2@test.com"]


[email_2]
sender = test_2@test.com
password = XXXXXXXXXXX
recipients= ["email_2@test.com", "email_2@test.com", "email_3@test.com"]

这些项将从main.py调用,返回它们各自的值。

包含functions_email.py函数的文件:

import smtplib,configparser,json
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText


def get_credentials(item):
parse = configparser.ConfigParser()
parse.read('config_email.ini')
sender = parse[item]['sender ']
password = parse[item]['password']
recipients= json.loads(parse[item]['recipients'])
return sender,password,recipients


def get_msg(sender,recipients,subject,mail_body):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
text = """\
"""+mail_body+""" """
part1 = MIMEText(text, "plain")
msg.attach(part1)
return msg


def send_email(msg,sender,password,recipients):
s = smtplib.SMTP('smtp.test.com')
s.login(sender,password)
s.sendmail(sender, recipients, msg.as_string())
s.quit()

文件main.py:

from functions_email import *


sender,password,recipients = get_credenciales('email_2')
subject= 'text to subject'
mail_body = 'body....................'
msg = get_msg(sender,recipients ,subject,mail_body)
send_email(msg,sender,password,recipients)

最好的问候!