此代码可以正常工作并向我发送电子邮件:
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
谁能帮我理解为什么?
我建议您将标准包 email
和 smtplib
一起使用来发送电子邮件。请看下面的例子(转载自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()
如您所见,MIMEText
对象中的标头 To
必须是由逗号分隔的电子邮件地址组成的字符串。另一方面,sendmail
函数的第二个参数必须是字符串列表(每个字符串都是一个电子邮件地址)。
因此,如果您有三个电子邮件地址:person1@example.com
、person2@example.com
和 person3@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 中获得任何地方,这是必须的 - 该文档对于标准库来说非常出色。
当我需要使用 Python 发送邮件时,我会使用 mailgun API,它在发送邮件时会遇到很多麻烦。他们有一个很棒的应用程序/api,可让您每月发送 5,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。
我想通过建议 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
安装它。
yagmail
吗?我正在尝试用于我自己的 SMTP 服务器。
Attachment
类并不重要;它仍然是同一件事。如果他们可以更改您的代码,那么他们无论如何都可以做任何他们想做的事情(有/没有root,这与发送电子邮件相同)。在我看来,这就像典型的“它很方便/神奇,所以它一定不太安全”。我很好奇你看到了什么真正的威胁?
有缩进问题。下面的代码将起作用:
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()
SERVER
变量是用户凭据。
email
库在这方面做得相当好(尤其是从 3.6 开始),尽管它仍然需要对您正在做的事情有所了解。
这是 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、网易邮箱,需要设置“客户端授权密码”,如下图:
https://i.stack.imgur.com/mUMOQ.png
参考:https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/3/library/email.examples.html#email-examples
在函数中缩进代码时(没关系),您还缩进了原始消息字符串的行。但是前导空白意味着标题行的折叠(连接),如 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.
这就是您的旧代码的工作原理和所做的工作。
请注意,我还保留了标题和正文之间的空行以适应 the RFC 的第 3.5 节(这是必需的),并根据 Python 样式指南 PEP-0008(这是可选的)将包含放在函数之外。
确保您已授予发件人和收件人在电子邮件帐户中发送和接收来自未知来源(外部来源)的电子邮件的权限。
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)
https://i.stack.imgur.com/43w6d.jpg
msg
不是有效的 SMTP 消息,如果您的邮件服务器接受它,它只会看起来消失在以太中。
它可能会在您的消息中添加标签。在将消息传递给 sendMail 之前打印出消息。
因为我刚刚弄清楚这是如何工作的,所以我想我会在这里输入我的两个位。
看来您没有在 SERVER 连接设置中指定端口,当我尝试连接到未使用默认端口的 SMTP 服务器时,这对我产生了一点影响:25。
根据 smtplib.SMTP 文档,您的 ehlo 或 helo 请求/响应应自动得到处理,因此您不必担心这一点(但如果所有其他方法都失败,则可能需要确认)。
要问自己的另一件事是您是否允许 SMTP 服务器本身上的 SMTP 连接?对于 GMAIL 和 ZOHO 等网站,您必须实际进入并激活电子邮件帐户中的 IMAP 连接。您的邮件服务器可能不允许不是来自“本地主机”的 SMTP 连接?有什么要调查的。
最后一件事是您可能想尝试在 TLS 上启动连接。大多数服务器现在都需要这种类型的身份验证。
您会看到我在电子邮件中塞入了两个收件人字段。 msg['TO'] 和 msg['FROM'] msg 字典项允许正确的信息显示在电子邮件本身的标题中,人们可以在电子邮件的接收端的 To/From 字段中看到这些信息(您甚至可以在此处添加回复字段。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()
另一个使用 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()
我编写了一个简单的函数 send_email()
,用于使用 smtplib
和 email
包发送电子邮件(链接到我的 article)。它还使用 dotenv
包来加载发件人的电子邮件和密码(请不要在代码中保密!)。我将 Gmail 用于电子邮件服务。密码是 App Password
(这里是关于如何生成 App Password
的 Google docs)。
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。
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
值得注意的是,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")
我对发送电子邮件的软件包选项并不满意,我决定制作并开源我自己的电子邮件发件人。它易于使用并且能够处理高级用例。
安装:
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_name
和 password
传递给 EmailSender
。
我在 send
方法中包含了很多功能:
包括附件
将图像直接包含到 HTML 正文中
Jinja 模板
开箱即用的更漂亮的 HTML 表格
文档:https://red-mail.readthedocs.io/en/latest/
源代码:https://github.com/Miksus/red-mail
在对示例进行了大量摆弄之后,例如 here 这现在对我有用:
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")
就您的代码而言,它似乎没有任何根本性的错误,只是不清楚您实际上是如何调用该函数的。我能想到的是,当您的服务器没有响应时,您将收到此 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 方法)。
", ".join(["a@example.com", "b@example.net"])
with
:with open(textfile, 'rb') as fp:
打开文件。可以删除显式关闭,因为with
块将关闭文件,即使其中发生错误也是如此。