小编典典

从(Python)代码实际发送邮件的正确方法是什么?

python

免责声明 :由于该问题的广泛性(请参阅下面的;-),我对标题犹豫不决,其他选项包括:

  • 如何仅使用Python代码从本地主机发送邮件?
  • 如何从Python代码发送电子邮件,而无需使用外部SMTP服务器?
  • 是否可以仅使用localhost和Python将电子邮件直接发送到目的地?

首先,介绍 一些内容
为了学习,我正在构建一个具有用户注册功能的网站。这个想法是,注册后用户将收到带有激活链接的电子邮件。我想编写和发送来自Python代码的电子邮件,这是我想要澄清的部分。


我的理解,在我开始之前 (显然是天真=)可以用这样的方式说明(假设存在合法的电子邮件地址 user@example.com ):

我的天真理解

搜索的例子后,我碰上#1)。从中我提取了以下代码片段,以编写并发送来自Python代码的电子邮件:

import smtplib

from email.message import EmailMessage

message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = 'me@example.com'
message['To'] = 'user@example.com'

smtp_server = smtplib.SMTP('smtp.server.address:587')
smtp_server.send_message(message)
smtp_server.quit()

下一个(显而易见的)问题是传递给smtplib.SMTP()而不是'smtp.server.address:587'。从对这个答案的评论中,我发现本地SMTP服务器(虽然仅用于测试目的)可以通过启动python3 -m smtpd -c DebuggingServer -n localhost:1025,然后smtp_server = smtplib.SMTP('smtp.server.address:587')可以更改为,smtp_server = smtplib.SMTP('localhost:1025')并且所有发送的电子邮件都将显示在控制台中(从中python3 -m smtpd -c DebuggingServer -n localhost:1025执行命令)。足以进行测试-这不是我想要的(我的目标是-
仅使用Python代码就能从本地计算机向“真实”电子邮件地址发送邮件的功能)。

因此,下一步将是设置本地SMTP服务器,该服务器能够将电子邮件发送到外部“真实世界”的电子邮件地址(因为我想全部通过Python代码来完成此操作,因此最好在以下位置实现服务器本身)
Python也是如此)。我记得在某杂志(2000年初)中读到的内容,即垃圾邮件发送者使用本地服务器发送邮件(该特定文章谈论的是Sambar,其开发已于2007年结束,并且不是用Python编写的:-)。应该是一些具有类似功能的当今解决方案。所以我开始搜索,我的希望是(在stackoverflow或其他地方)找到一个相当短的代码段,该段代码可以满足我的要求。我没有找到这样的代码段,但是遇到了一个标题为(Python)的不带邮件服务器的发送电子邮件的代码段(使用chilkat
API)(尽管我所需要的(据说)就在那儿,但在代码注释中,第一行明确指出:

是否真的可以在不连接邮件服务器的情况下发送电子邮件?并不是的。

以及以下几行:

以下是声称不需要邮件服务器的其他组件内部发生的情况:该组件使用目标收件人的电子邮件地址进行DNS
MX查找,以查找该域的邮件服务器(即SMTP服务器)。然后,它连接到该服务器并发送电子邮件。您仍在连接到SMTP服务器-而不是服务器。

读完这些,使我理解了-
很明显,我对过程的理解(如上图所示)缺少一些细节。为了解决这个问题,我已经阅读了SMTP上的整个RFC


阅读完RFC之后, 对过程的 理解 会变得更加生动:
我加深了理解


通过这种理解,我想澄清一些 实际的问题

  1. 从总体上看,我的“ 加深理解 ”可以被认为是正确的吗?
  2. MX查找返回的确切地址是什么?

需要完整的Gmail或G Suite电子邮件地址才能进行身份验证。

* 但是,如果将电子邮件发送到`user@gmail.com`非gmail拥有的SMTP服务器,则它将被重定向到gmail服务器之一(直接或通过网关/中继)。在这种情况下(我假设),电子邮件的发件人只需要在邮件提交时进行身份验证,那么在此之后,gmail服务器将接受未经身份验证的邮件吗?

  * 如果是,是什么使我无法“假装”成为这样的网关/中继并将电子邮件直接移交给其指定的SMTP?然后,编写“ proxy-SMTP”也应该非常容易,它将通过MX查找来搜索适当的服务器,然后直接向其发送电子邮件。
  1. gmail SMTP上的文档还提到了aspmx.l.google.com服务器,该服务器不需要身份验证,但是:

邮件只能发送给Gmail或G Suite用户。

话虽如此,我认为以下代码段应该可以正常工作,以便将邮件提交到ExistingUser@gmail.com邮箱:

    import smtplib

from email.message import EmailMessage

message = EmailMessage()
message.set_content('Message test content')
message['Subject'] = 'Test mail!'
message['From'] = 'me@whatever.com'
message['To'] = 'ExistingUser@gmail.com'

smtp_server = smtplib.SMTP('aspmx.l.google.com:25')
smtp_server.send_message(message)
smtp_server.quit()

运行后,上面的代码(ExistingUser@gmail.com由有效的邮件替换)将抛出OSError: [Errno 65] No route to host。我要在这里确认的是与aspmx.l.google.com代码的通信已正确处理。


阅读 214

收藏
2021-01-20

共1个答案

小编典典


我将一一讨论这些问题:

  1. 是的,一般情况下,电子邮件的发送可以这样描述: 我加深了理解

  2. MX查找返回服务器的地址,这些地址接收发往指定域的电子邮件。

    • 至于“为什么smtp-relay.gmail.comsmtp.gmail.comaspmx.l.google.com不被返回host -t mx gmail.com的命令?”。关于这一点的另一答案几乎涵盖了这一点。这里要掌握的要点是:
    • MX查找返回的服务器负责 接收 该域的电子邮件(在这种情况下为gmail)
    • gmail文档中列出的服务器是用于邮件 发送的 (即gmail用户想要发送给其他gmail用户的邮件,否则将提交给这些服务器)
    • 对于接收电子邮件(即MX查找返回的电子邮件)的服务器,不需要身份验证。

    • 有几件事可以防止此类服务器被滥用:

    • 许多ISP阻止到端口25(这是邮件接收服务器的默认端口)的出站连接,以防止此类“ 直接 ”邮件发送
    • 接收服务器方面采取了许多措施,主要是为了防止垃圾邮件,但是结果也可能会阻止这种“ 直接 ”邮件发送(一些示例是:DNSBL-阻止列表IPs,DKIM —是一种电子邮件身份验证方法,旨在检测电子邮件中的伪造发件人地址(如果您没有自己的合法邮件服务器,则将他人的域用作From字段,这可能会使您受到DKIM的攻击)
    • 代码段还可以。由于ISP端的阻塞,很可能产生该错误。

说了这么多,代码片段:

import smtplib

from email.message import EmailMessage

message = EmailMessage()
message.set_content('Message content here')
message['Subject'] = 'Your subject here'
message['From'] = 'me@example.com'
message['To'] = 'user@example.com'

smtp_server = smtplib.SMTP('smtp.server.address:25')
smtp_server.send_message(message)
smtp_server.quit()

会实际发送一封电子邮件,因为该smtp.server.address:25服务器是合法服务器,并且在ISP和/或smtp.server.address方面没有任何阻塞。

2021-01-20