使用 smtp 和 Python 发送电子邮件时出现 SSL 错误

Posted

技术标签:

【中文标题】使用 smtp 和 Python 发送电子邮件时出现 SSL 错误【英文标题】:SSL error while sending email using smtp with Python 【发布时间】:2021-12-01 03:03:12 【问题描述】:

我想使用 Outlook 发送电子邮件。代码如下:

import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg['From'] = '*******'
msg['Subject'] = 'Some subject here'
msg['To'] = '********'
        
msg.set_content('Some text here')

with smtplib.SMTP_SSL('smtp-mail.outlook.com', 587) as smtp:
    smtp.login('******', '****')
    smtp.send_message(msg)
    print('Email sent!')

我收到以下错误:

---------------------------------------------------------------------------
SSLError                                  Traceback (most recent call last)
<ipython-input-8-4d5956f55c88> in <module>
      6 msg.set_content('Some text here')
      7 
----> 8 with smtplib.SMTP_SSL('smtp-mail.outlook.com', 587) as smtp:
      9     smtp.login('sender_email', 'password')
     10     smtp.send_message(msg)

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in __init__(self, host, port, local_hostname, keyfile, certfile, timeout, source_address, context)
   1029             self.context = context
   1030             SMTP.__init__(self, host, port, local_hostname, timeout,
-> 1031                     source_address)
   1032 
   1033         def _get_socket(self, host, port, timeout):

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in __init__(self, host, port, local_hostname, timeout, source_address)
    249 
    250         if host:
--> 251             (code, msg) = self.connect(host, port)
    252             if code != 220:
    253                 self.close()

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in connect(self, host, port, source_address)
    334         if self.debuglevel > 0:
    335             self._print_debug('connect:', (host, port))
--> 336         self.sock = self._get_socket(host, port, self.timeout)
    337         self.file = None
    338         (code, msg) = self.getreply()

~/anaconda/envs/quant2/lib/python3.6/smtplib.py in _get_socket(self, host, port, timeout)
   1037                     self.source_address)
   1038             new_socket = self.context.wrap_socket(new_socket,
-> 1039                                                   server_hostname=self._host)
   1040             return new_socket
   1041 

~/anaconda/envs/quant2/lib/python3.6/ssl.py in wrap_socket(self, sock, server_side, do_handshake_on_connect, suppress_ragged_eofs, server_hostname, session)
    405                          suppress_ragged_eofs=suppress_ragged_eofs,
    406                          server_hostname=server_hostname,
--> 407                          _context=self, _session=session)
    408 
    409     def wrap_bio(self, incoming, outgoing, server_side=False,

~/anaconda/envs/quant2/lib/python3.6/ssl.py in __init__(self, sock, keyfile, certfile, server_side, cert_reqs, ssl_version, ca_certs, do_handshake_on_connect, family, type, proto, fileno, suppress_ragged_eofs, npn_protocols, ciphers, server_hostname, _context, _session)
    815                         # non-blocking
    816                         raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets")
--> 817                     self.do_handshake()
    818 
    819             except (OSError, ValueError):

~/anaconda/envs/quant2/lib/python3.6/ssl.py in do_handshake(self, block)
   1075             if timeout == 0.0 and block:
   1076                 self.settimeout(None)
-> 1077             self._sslobj.do_handshake()
   1078         finally:
   1079             self.settimeout(timeout)

~/anaconda/envs/quant2/lib/python3.6/ssl.py in do_handshake(self)
    687     def do_handshake(self):
    688         """Start the SSL/TLS handshake."""
--> 689         self._sslobj.do_handshake()
    690         if self.context.check_hostname:
    691             if not self.server_hostname:

SSLError: [SSL: WRONG_VERSION_NUMBER] wrong version number (_ssl.c:852)

【问题讨论】:

我更新了我的答案。顺便说一句,您使用的是免费的 Outlook 帐户还是企业帐户? 【参考方案1】:

Microsoft Outlook 在发送电子邮件时使用STARTTLS。所以你需要用smtplib.SMTP替换smtplib.SMTP_SSL,你需要调用starttls

参考:smtplib.SMTP.starttls

import smtplib
from email.message import EmailMessage

sender = 'somename@outlook.com'
recipient = 'somename@gmail.com'

msg = EmailMessage()
msg.set_content('this is a test')
msg['From'] = 'somename@outlook.com'
msg['To'] = 'somename@gmail.com'
msg['Subject'] = 'test email'

with smtplib.SMTP('smtp.office365.com', 587) as server:
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.ehlo()
    server.login('your_login', "your_password", initial_response_ok=True) 
    server.ehlo()
    server.sendmail(sender, recipient, msg.as_string())
    print('Email sent!')
    server.close()

这是我的 Gmail 帐户中的 Outlook 邮件。

我注意到我必须更改我的 Outlook 密码,因为它有一个 \n,Python 将其读取为一个新行。

----------------------------------------
My system information
----------------------------------------
Platform:       macOS
OS Version:     10.15.7
Python Version: 3.9
----------------------------------------

您的问题并未确定您拥有的 Outlook 帐户类型。

    免费帐户 公司帐户

您在下面的 cmets 中声明您的错误消息包括 ask your email administrator. 我在 免费帐户 中似乎没有此消息,所以我假设您可能有一个 公司帐户. 如果您有后者,请查看此Enable or disable authenticated client SMTP submission,因为电子邮件管理员需要在您的公司帐户上启用Authenticated SMTP

【讨论】:

它给我一个错误提示“无法验证”并询问您的电子邮件管理员 @EchchamaNayak 我更新了我的答案。我可以多次从我的 Outlook 帐户向我的 Gmail 帐户发送一封包含此答案的电子邮件。 我确实用的是公司账号,不好意思忘记提了 是的,这是一个重要的细节,可以减少这个赏金问题的工作量。希望您能够说服您的管理员为您启用此功能。【参考方案2】:

用户“生活很复杂”的回答几乎是正确的。我想从我身边添加一些可能会有所帮助的东西。我不太确定您在这里使用的是哪个 python 版本。我猜它是 Python 3.X。您需要根据您在案例中使用的实际 python 版本进行检查。

来自 Python smtplib 文档,link。请根据以下指南检查您使用的 python 版本。

类 smtplib.SMTP_SSL(host='', port=0, local_hostname=None, 密钥文件=无,证书文件=无,[超时,]上下文=无, source_address=None) SMTP_SSL 实例的行为与 SMTP 的实例。 SMTP_SSL 应该用于 SSL 的情况 需要从连接开始并使用 starttls() 是 不适当。如果未指定主机,则使用本地主机。如果 端口为零,则使用标准 SMTP-over-SSL 端口 (465)。这 可选参数 local_hostname、timeout 和 source_address 具有 与它们在 SMTP 类中的含义相同。 context,也是可选的,可以 包含 SSLContext 并允许配置 安全连接。请阅读安全注意事项以获得最佳效果 实践。

keyfile 和 certfile 是 context 的传统替代方案,可以 指向 PEM 格式的私钥和证书链文件 SSL 连接。

3.3 版更改:添加了上下文。

3.3 版更改:添加了 source_address 参数。

在 3.4 版更改:该类现在支持主机名检查 ssl.SSLContext.check_hostname 和服务器名称指示(请参阅 ssl.HAS_SNI)。

3.6 版后已弃用:keyfile 和 certfile 在 赞成上下文。请改用 ssl.SSLContext.load_cert_chain(), 或者让 ssl.create_default_context() 选择系统信任的 CA 给你的证书。

在 3.9 版更改:如果超时参数设置为零,则 将引发 ValueError 以防止创建非阻塞 插座

您需要检查服务器端是否启用了 SSLV3。查看此链接,看看哪个客户端版本可以先连接到哪个服务器版本SSL Version Compatibility。

import smtplib
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLS) 
#Above line is required to switch default SSLV3 protocol to TLS, recommended by python docs
# If you want to use SSLV3 and you are sure that it is enabled on server end then use 
#context = ssl.SSLContext(ssl.PROTOCOL_SSLv3)
connection = smtplib.SMTP('smtp-mail.outlook.com', 587)
connection.ehlo()
connection.starttls(context=context)
connection.ehlo()
connection.login('now_your_real_login_data@outlook.com', 'otherwise_SMTPServerDisconnect')

【讨论】:

我修改了我的答案。我删除了 SSLContext 部分并将我的密码更改为不包含换行符的密码。我能够正确无误地发送电子邮件。

以上是关于使用 smtp 和 Python 发送电子邮件时出现 SSL 错误的主要内容,如果未能解决你的问题,请参考以下文章

在 Windows XP 中使用 Indy 10 发送带有内嵌图像的电子邮件时出现“SMTP 传入数据超时”

从第三方 HTML 控件使用 Google SMTP 发送邮件时出现问题

创建 smtp() 时出现 Python smtplib 错误:“utf-8”编解码器无法解码字节

尝试通过 Office 365 发送电子邮件时出现 SMTP 5.7.57 错误

通过未加密的连接发送电子邮件

谷歌在Python发送邮件的SMTP问题,怎么解决