繁体   English   中英

如何用 Python 发送 email?

[英]How to send an email with Python?

此代码有效并向我发送 email 就好了:

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

但是,如果我尝试将其包装在 function 中,如下所示:

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

谁能帮我理解为什么?

我建议您使用标准包emailsmtplib一起发送 email。 请看下面的例子(转载自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()

要将 email 发送到多个目的地,您还可以按照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 object 中的MIMEText To必须是由逗号分隔的 email 地址组成的字符串。 另一方面, sendmail function 的第二个参数必须是一个字符串列表(每个字符串都是一个 email 地址)。

So, if you have three email addresses: person1@example.com , person2@example.com , and person3@example.com , you can do as follows (obvious sections omitted):

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 中找到任何地方,这是必须的 - 该文档对于标准库来说非常出色。

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

发送 email 如下所示:

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

您还可以跟踪事件等等,请参阅快速入门指南

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

您的整个代码将是:

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

请注意,我为所有 arguments 提供了默认值,例如,如果您想发送给自己,则可以省略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()

这是 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.')

称之为 function:

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

以下仅适用于中国用户:

如果使用126/163、网易邮箱,需要设置“客户端授权密码”,如下图:

在此处输入图像描述

参考: https://stackoverflow.com/a/41470149/2803344 https://docs.python.org/

尝试这个:

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

在 function 中缩进代码时(没关系),您还缩进了原始消息字符串的行。 但前导空白意味着 header 行的折叠(连接),如RFC 2822 - Internet Message Format的第 2.2.3 和 3.2.3 节所述:

每个 header 字段在逻辑上都是由字段名称、冒号和字段正文组成的单行字符。 然而,为了方便起见,并处理每行 998/78 个字符的限制,header 字段的字段主体部分可以拆分为多行表示; 这称为“折叠”。

在您的sendmail调用的 function 形式中,所有行都以空格开头,因此“展开”(连接)并且您正在尝试发送

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

除了我们的想法之外, smtplib将不再理解To:Subject:标头,因为这些名称仅在行首被识别。 相反, smtplib将假定一个非常长的发送方 email 地址:

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

这不起作用,所以你的例外。

解决方案很简单:只保留以前的message字符串。 这可以通过 function (如 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 (这是可选的)将包含放在 function 之外。

确保您已授予发送者和接收者发送 email 并从 Email 帐户中的未知来源(外部来源)接收 email 的权限。

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)

在此处输入图像描述

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

因为我刚刚弄清楚这是如何工作的,所以我想我会在这里输入我的两个位。

看来您没有在 SERVER 连接设置中指定端口,当我尝试连接到未使用默认端口的 SMTP 服务器时,这对我产生了一点影响:25。

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

要问自己的另一件事是您是否允许 SMTP 服务器本身上的 SMTP 连接? 对于像 GMAIL 和 ZOHO 这样的网站,您实际上必须在 go 中激活 email 帐户中的 IMAP 连接。 您的邮件服务器可能不允许不是来自“本地主机”的 SMTP 连接? 有什么要调查的。

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

您会看到我在 email 中插入了两个 TO 字段。 msg['TO'] 和 msg['FROM'] msg 字典项允许正确的信息显示在 email 本身的标题中,可以在 email 的接收端的 To/From 字段中看到(您甚至可以在这里添加一个回复字段。TO 和 FROM 字段本身就是服务器所需要的。我知道我听说一些 email 服务器如果没有正确的 email 标头会拒绝电子邮件。

这是我在 function 中使用的代码,它适用于 email 使用我的本地计算机和远程 ZC2239A92BDE29F0A9F9173193CC2FE00 服务器的 *.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()
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")

我对用于发送电子邮件的 package 选项不满意,我决定制作并开源我自己的 email 发件人。 它易于使用并且能够处理高级用例。

安装:

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

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

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

  • 包括附件
  • 将图像直接包含到 HTML 主体
  • Jinja 模板
  • 开箱即用的更漂亮的 HTML 表

文档: https://red-mail.readthedocs.io/en/latest/

源码: 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")

我写了一个简单的 function send_email()用于 email 发送与smtplibemail包(链接到我的文章)。 它还使用dotenv package 加载发件人 email 和密码(请不要在代码中保密。)。 我将 Gmail 用于 email 服务。 密码是App Password (这里是Google docs on how to generate 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

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

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

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

我建议使用发件人的 email 配置、密码和收件人的配置文件(可以是.json、.yml、.ini 等)。

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

下面是一个包含 3 个文件、配置、函数和主文件的小示例。 纯文本邮件。

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)

此致!

就您的代码而言,它似乎没有任何根本性的错误,只是不清楚您实际上是如何调用 function 的。 我能想到的是,当您的服务器没有响应时,您将收到此 SMTPServerDisconnected 错误。 如果你在 smtplib 中查找 getreply() function(摘录如下),你就会明白了。

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.
    """

check an example at https://github.com/rreddy80/sendEmails/blob/master/sendEmailAttachments.py that also uses a function call to send an email, if that's what you're trying to do (DRY approach).

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM