使用SMTP从Python发送邮件。

136

我正在使用以下方法通过SMTP从Python发送邮件。这是正确的方法吗?还是有什么需要注意的地方?

from smtplib import SMTP
import datetime

debuglevel = 0

smtp = SMTP()
smtp.set_debuglevel(debuglevel)
smtp.connect('YOUR.MAIL.SERVER', 26)
smtp.login('USERNAME@DOMAIN', 'PASSWORD')

from_addr = "John Doe <john@doe.net>"
to_addr = "foo@bar.com"

subj = "hello"
date = datetime.datetime.now().strftime( "%d/%m/%Y %H:%M" )

message_text = "Hello\nThis is a mail from your server\n\nBye\n"

msg = "From: %s\nTo: %s\nSubject: %s\nDate: %s\n\n%s" 
        % ( from_addr, to_addr, subj, date, message_text )

smtp.sendmail(from_addr, to_addr, msg)
smtp.quit()

3
请确保你正确获取日期/时间。我发现下面这个函数非常有用,可以为日期头提供格式完美的值:http://docs.python.org/py3k/library/email.util.html#email.utils.formatdate - BastiBen
这是一个代码示例,允许在主题和/或正文中发送Unicode文本的电子邮件。链接 - jfs
这里有一个代码示例,演示了如何内联发送图片(以及同时包含 HTML 和纯文本部分的电子邮件)。它还展示了如何在旧版 Python 上配置 SSL 参数。 - jfs
2
请注意,有可用的包装库可以使发送电子邮件的代码量大大减少(例如yagmail)。 - PascalVKooten
这回答解决了你的问题吗?[如何使用Python发送电子邮件?](https://dev59.com/G2025IYBdhLWcg3wAg_p) - Serge Stroobandt
16个回答

136
我使用的脚本非常相似,我在这里发布它作为如何使用email.*模块生成MIME消息的示例;因此,这个脚本可以很容易地修改以附加图片等内容。
我依赖于我的ISP添加日期时间标头。
我的ISP要求我使用安全的SMTP连接发送邮件,我依赖于smtplib模块(可在http://www1.cs.columbia.edu/~db2501/ssmtplib.py下载)。
与您的脚本一样,用于在SMTP服务器上进行身份验证的用户名和密码(下面给出虚拟值)以明文形式在源中。这是一个安全性弱点;但最好的选择取决于您对保护这些内容的谨慎程度。
=======================================
#! /usr/local/bin/python


SMTPserver = 'smtp.att.yahoo.com'
sender =     'me@my_email_domain.net'
destination = ['recipient@her_email_domain.com']

USERNAME = "USER_NAME_FOR_INTERNET_SERVICE_PROVIDER"
PASSWORD = "PASSWORD_INTERNET_SERVICE_PROVIDER"

# typical values for text_subtype are plain, html, xml
text_subtype = 'plain'


content="""\
Test message
"""

subject="Sent from Python"

import sys
import os
import re

from smtplib import SMTP_SSL as SMTP       # this invokes the secure SMTP protocol (port 465, uses SSL)
# from smtplib import SMTP                  # use this for standard SMTP protocol   (port 25, no encryption)

# old version
# from email.MIMEText import MIMEText
from email.mime.text import MIMEText

try:
    msg = MIMEText(content, text_subtype)
    msg['Subject']=       subject
    msg['From']   = sender # some SMTP servers will do this automatically, not all

    conn = SMTP(SMTPserver)
    conn.set_debuglevel(False)
    conn.login(USERNAME, PASSWORD)
    try:
        conn.sendmail(sender, destination, msg.as_string())
    finally:
        conn.quit()

except:
    sys.exit( "mail failed; %s" % "CUSTOM_ERROR" ) # give an error message

1
@Vincent:邮件发送失败;'module'对象没有属性'SSLFakeSocket' - 使用Gmail :( - RadiantHex
2
使用smtplib.SMTP_SSL(Python的最新版本中标准模块)创建连接,而不是ssmtplib.STMP_SSL(上面提到的第三方模块)。请注意,标准模块以单个“s”开头。这对我很有效。 - Julio Gorgé
3
from ssmtplib import SMTP_SSL as SMTP替换为from smtplib import SMTP_SSL as SMTP,这个例子将可以在标准的Python库中工作。 - Adam Matan
9
添加 msg['To'] = ','.join(destination),否则在 Gmail 中不会显示收件人。 - Taha Jahangir
这个答案可以通过遵循PEP8中的一致引用风格来改进。 - Langston
显示剩余4条评论

103

我通常使用的方法...和之前的方法差别不大,只是有一点点不同

import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText

msg = MIMEMultipart()
msg['From'] = 'me@gmail.com'
msg['To'] = 'you@gmail.com'
msg['Subject'] = 'simple email in python'
message = 'here is the email'
msg.attach(MIMEText(message))

mailserver = smtplib.SMTP('smtp.gmail.com',587)
# identify ourselves to smtp gmail client
mailserver.ehlo()
# secure our email with tls encryption
mailserver.starttls()
# re-identify ourselves as an encrypted connection
mailserver.ehlo()
mailserver.login('me@gmail.com', 'mypassword')

mailserver.sendmail('me@gmail.com','you@gmail.com',msg.as_string())

mailserver.quit()

就是这样


如果您使用了两步验证,您必须首先创建一个应用程序专用密码,并将其替换为您的普通密码。请参阅使用应用程序密码登录 - Suzana
4
我同意,这是最好的答案,应该被接受。实际上被接受的那个答案较差。 - ABCD
15
对于Python3,请使用以下代码:from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText - art

23

如果您想使用TLS进行SMTP身份验证而不是SSL,则只需更改端口(使用587)并执行smtp.starttls()。这对我有效:

...
smtp.connect('YOUR.MAIL.SERVER', 587)
smtp.ehlo()
smtp.starttls()
smtp.ehlo()
smtp.login('USERNAME@DOMAIN', 'PASSWORD')
...

7
这个怎么样?
import smtplib

SERVER = "localhost"

FROM = "sender@example.com"
TO = ["user@example.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(SERVER)
server.sendmail(FROM, TO, message)
server.quit()

6
我看到的主要问题是你没有处理任何错误:.login().sendmail()都有可能抛出文档中记录的异常,而且似乎.connect()必须有一种方式来指示它无法连接 - 可能是由基础套接字代码抛出的异常。

6

请确保没有任何防火墙阻止SMTP。第一次尝试发送电子邮件时,Windows防火墙和McAfee都会阻止它 - 花费了很长时间才找到它们。


6

以下代码对我来说运行良好:

import smtplib
 
to = 'mkyong2002@yahoo.com'
gmail_user = 'mkyong2002@gmail.com'
gmail_pwd = 'yourpassword'
smtpserver = smtplib.SMTP("smtp.gmail.com",587)
smtpserver.ehlo()
smtpserver.starttls()
smtpserver.ehlo()
smtpserver.login(gmail_user, gmail_pwd)
header = 'To:' + to + '\n' + 'From: ' + gmail_user + '\n' + 'Subject:testing \n'
print header
msg = header + '\n this is test msg from mkyong.com \n\n'
smtpserver.sendmail(gmail_user, to, msg)
print 'done!'
smtpserver.quit()

参考:http://www.mkyong.com/python/how-do-send-email-in-python-via-smtplib/

这篇文章介绍了如何使用Python中的smtplib模块发送电子邮件。该模块提供了SMTP客户端实现,可以通过SMTP服务器发送邮件。本文还提供了一个示例程序,演示如何在Python中设置SMTP服务器并发送电子邮件。如果您需要在Python中编写发送电子邮件的应用程序,这篇文章将对您有所帮助。

1
Flask有一个邮件框架:从flask.ext.mail导入Mail。我正在排除故障,想回到Python代码看看能否让它起作用。我喜欢这个答案,因为它非常简明扼要。哦,是的,它起作用了! - user3842449
注意:之前的答案版本包括以下行:smtpserver.close()。它必须是:smtpserver.quit(),因为close()无法正确终止TLS连接! close()将在quit()期间调用。 - aronadaal
你好,我在运行上面的命令时遇到了问题。当我使用smtpserver.starttls()时,我收到了一个SMTP错误“SMTPServerDisconnected: Connection unexpectedly closed: [Errno 10054]”,可以在以下链接中查看:https://stackoverflow.com/questions/46094175/starttls-is-giving-me-a-smtpserverdisconnect-error-when-trying-to-send-emails - fazkan

4

这是我用SMTP发送邮件的示例代码。

import smtplib, ssl

smtp_server = "smtp.gmail.com"
port = 587  # For starttls
sender_email = "sender@email"
receiver_email = "receiver@email"
password = "<your password here>"
message = """ Subject: Hi there

This message is sent from Python."""


# Create a secure SSL context
context = ssl.create_default_context()

# Try to log in to server and send email
server = smtplib.SMTP(smtp_server,port)

try:
    server.ehlo() # Can be omitted
    server.starttls(context=context) # Secure the connection
    server.ehlo() # Can be omitted
    server.login(sender_email, password)
    server.sendmail(sender_email, receiver_email, message)
except Exception as e:
    # Print any error messages to stdout
    print(e)
finally:
    server.quit()

4

或者

import smtplib
 
from email.message import EmailMessage
from getpass import getpass


password = getpass()

message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = "USERNAME@DOMAIN"
message['To'] = "you@mail.com"

try:
    smtp_server = None
    smtp_server = smtplib.SMTP("YOUR.MAIL.SERVER", 587)
    smtp_server.ehlo()
    smtp_server.starttls()
    smtp_server.ehlo()
    smtp_server.login("USERNAME@DOMAIN", password)
    smtp_server.send_message(message)
except Exception as e:
    print("Error: ", str(e))
finally:
    if smtp_server is not None:
        smtp_server.quit()

如果您想使用端口465,您需要创建一个SMTP_SSL对象。

4

基于这个例子,我制作了以下函数:

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def send_email(host, port, user, pwd, recipients, subject, body, html=None, from_=None):
    """ copied and adapted from
        https://dev59.com/HWkw5IYBdhLWcg3wGGm8
    returns None if all ok, but if problem then returns exception object
    """

    PORT_LIST = (25, 587, 465)

    FROM = from_ if from_ else user 
    TO = recipients if isinstance(recipients, (list, tuple)) else [recipients]
    SUBJECT = subject
    TEXT = body.encode("utf8") if isinstance(body, unicode) else body
    HTML = html.encode("utf8") if isinstance(html, unicode) else html

    if not html:
        # Prepare actual message
        message = """From: %s\nTo: %s\nSubject: %s\n\n%s
        """ % (FROM, ", ".join(TO), SUBJECT, TEXT)
    else:
                # https://dev59.com/rHNA5IYBdhLWcg3wpfiu
        msg = MIMEMultipart('alternative')
        msg['Subject'] = SUBJECT
        msg['From'] = FROM
        msg['To'] = ", ".join(TO)

        # Record the MIME types of both parts - text/plain and text/html.
        # utf-8 -> https://dev59.com/ZG025IYBdhLWcg3wl3O-
        part1 = MIMEText(TEXT, 'plain', "utf-8")
        part2 = MIMEText(HTML, 'html', "utf-8")

        # Attach parts into message container.
        # According to RFC 2046, the last part of a multipart message, in this case
        # the HTML message, is best and preferred.
        msg.attach(part1)
        msg.attach(part2)

        message = msg.as_string()


    try:
        if port not in PORT_LIST: 
            raise Exception("Port %s not one of %s" % (port, PORT_LIST))

        if port in (465,):
            server = smtplib.SMTP_SSL(host, port)
        else:
            server = smtplib.SMTP(host, port)

        # optional
        server.ehlo()

        if port in (587,): 
            server.starttls()

        server.login(user, pwd)
        server.sendmail(FROM, TO, message)
        server.close()
        # logger.info("SENT_EMAIL to %s: %s" % (recipients, subject))
    except Exception, ex:
        return ex

    return None

如果您只传递 body参数,则会发送纯文本邮件,但如果您同时传递htmlbody 参数,则会发送html格式的电子邮件(对于不支持html / mime类型的电子邮件客户端,会回退到文本内容)。
示例用法:
ex = send_email(
      host        = 'smtp.gmail.com'
   #, port        = 465 # OK
    , port        = 587  #OK
    , user        = "xxx@gmail.com"
    , pwd         = "xxx"
    , from_       = 'xxx@gmail.com'
    , recipients  = ['yyy@gmail.com']
    , subject     = "Test from python"
    , body        = "Test from python - body"
    )
if ex: 
    print("Mail sending failed: %s" % ex)
else:
    print("OK - mail sent"

顺便提一句,如果你想使用Gmail作为测试或生产SMTP服务器,请启用较不安全的应用程序的临时或永久访问权限:


1
请注意,从2022年5月30日起,您必须使用两步验证来连接您的Google账户。允许访问不太安全的应用程序已经不再是一个选项了 :) - Ilan Yashuk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接