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

SSL error while sending email using smtp with Python

我想使用 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)

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 帐户类型。

  1. 免费帐户
  2. 企业账户

你在下面的评论中说你的错误消息包括 ask your email administrator. 我没有在 免费帐户 中看到这条消息所以我假设你可能拥有 公司帐户。 如果您有后者,请查看此 Enable or disable authenticated client SMTP submission,因为电子邮件管理员需要在您的公司帐户上启用 Authenticated SMTP

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

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

class smtplib.SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None, [timeout, ]context=None, source_address=None) An SMTP_SSL instance behaves exactly the same as instances of SMTP. SMTP_SSL should be used for situations where SSL is required from the beginning of the connection and using starttls() is not appropriate. If host is not specified, the local host is used. If port is zero, the standard SMTP-over-SSL port (465) is used. The optional arguments local_hostname, timeout and source_address have the same meaning as they do in the SMTP class. context, also optional, can contain a SSLContext and allows configuring various aspects of the secure connection. Please read Security considerations for best practices.

keyfile and certfile are a legacy alternative to context, and can point to a PEM formatted private key and certificate chain file for the SSL connection.

Changed in version 3.3: context was added.

Changed in version 3.3: source_address argument was added.

Changed in version 3.4: The class now supports hostname check with ssl.SSLContext.check_hostname and Server Name Indication (see ssl.HAS_SNI).

Deprecated since version 3.6: keyfile and certfile are deprecated in favor of context. Please use ssl.SSLContext.load_cert_chain() instead, or let ssl.create_default_context() select the system’s trusted CA certificates for you.

Changed in version 3.9: If the timeout parameter is set to be zero, it will raise a ValueError to prevent the creation of a non-blocking socket

您需要检查服务器端是否启用了SSLV3。查看此 link 以查看哪个客户端版本可以首先连接到哪个服务器版本 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')