#247 SMTP 拓展
SMTP Email 2018-04-24RFC#821 定义的 SMTP 协议非常简单(简陋)。
1993 年,RFC#1425 SMTP Service Extensions 定义了 SMTP 协议的拓展框架。
这个向前兼容的安全拓展框架是通过 EHLO 命令来实现。
coding in a complicated world
RFC#821 定义的 SMTP 协议非常简单(简陋)。
1993 年,RFC#1425 SMTP Service Extensions 定义了 SMTP 协议的拓展框架。
这个向前兼容的安全拓展框架是通过 EHLO 命令来实现。
注意:这边不是讨论 邮箱地址的格式。
From 发信人To 收信人Cc 抄送人Bcc 密送人Rely-To 回复地址Sender 发信人Return-Path / Reverse-Path / Envelope-From碳式复写纸 carbon paper
副本,抄送 carbon copy => CC
密送 blind carbon copy => BCC
按照设计,密送地址不希望被其他收信人、抄送人察觉,只是密送地址才知道自己是密送。
SMTP 服务器不处理 CC、BCC,SMTP 客户端应该自行处理
TO 地址 + CC 地址 + BCC 地址一起放到 SMTP 会话的 RCPT TO 字段
所以,按照我的理解,邮件客户端:
在一次 SMTP 会话中,如果有 3 个 TO/CC 地址,2 个 BCC 地址,应该对那 3 个地址批量发送,然后对那 2 个 BCC 地址分别加上 BCC 头,分别发送。
更稳妥一点:如果是批量发送邮件,不要放 BCC 到邮件头!!!显示一个 密送:xxx 也没啥意义。
from_addr = "from@markjour.com"
to_addrs = ["to@markjour.com"]
cc_addrs = ["cc1@markjour.com", "cc2@markjour.com"]
bcc_addrs = ["bcc@markjour.com"]
msg = f"""
From: {from_addr}
To: {", ".join(to_addrs)}
Cc: {", ".join(cc_addrs)}
Hello World
""".strip()
send_to = to_addrs + cc_addrs + bcc_addrs
server = smtplib.SMTP('smtp.126.com')
server.set_debuglevel(1)
server.login(api_user, api_key)
server.sendmail(from_addr, send_to, msg)
server.quit()
import logging
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
logging.basicConfig(level=logging.DEBUG)
SMTP_HOST = 'smtp.example.com'
SMTP_PORT = 587
SMTP_USERNAME = 'your_username'
SMTP_PASSWORD = 'your_password'
SMTP_STARTTLS = True
sender = 'sender@example.com'
recipients = ['rcpt01@example.com', '中国 <rcpt02@example.com>']
subject = 'Test Email'
content_text = 'This is a test email.'
content_html = '<html><h1>Hello</h1></html>'
def encode_recipient(name, addr):
pass
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = ', '.join(recipients)
msg['Subject'] = subject
msg.attach(MIMEText(content_text))
msg.attach(MIMEText(content_html))
print(msg.as_string())
def smtp_debug(self, *args):
msg = ' '.join(map(str, args))
logging.debug(msg)
smtp = smtplib.SMTP(SMTP_HOST, SMTP_PORT)
smtp._print_debug = smtp_debug
smtp.set_debuglevel(1)
smtp.ehlo()
if SMTP_STARTTLS:
smtp.starttls()
smtp.ehlo()
if SMTP_USERNAME and SMTP_USERNAME:
smtp.login(SMTP_USERNAME, SMTP_PASSWORD)
smtp.sendmail(sender, recipients, msg.as_string())
smtp.quit()
比如:
Sun, 20 Jun 2018 00:47:04 -0700 (PDT)
Thu, 10 Jun 2021 16:10:03 -0700 (PDT)
Thu, 10 Jun 2021 08:06:31 -0700 (PDT)
定义在 RFC 822 的 5. DATE AND TIME SPECIFICATION。
date-time = [ day "," ] date time ; dd mm yy hh:mm:ss zzz
day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"
date = 1*2DIGIT month 2DIGIT ; day month year e.g. 20 Jun 82
month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
time = hour zone ; ANSI and Military
hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
; 00:00:00 - 23:59:59
zone = "UT" / "GMT" ; Universal Time
; North American : UT
/ "EST" / "EDT" ; Eastern: - 5/ - 4
/ "CST" / "CDT" ; Central: - 6/ - 5
/ "MST" / "MDT" ; Mountain: - 7/ - 6
/ "PST" / "PDT" ; Pacific: - 8/ - 7
/ 1ALPHA ; Military: Z = UT;
; A:-1; (J not used)
; M:-12; N:+1; Y:+12
/ ( ("+" / "-") 4DIGIT ) ; Local differential
; hours+min. (HHMM)
总结就是:
[day-of-week,] day month year hour:minute[:second] timezone
UT、GMT、EST、EDT、CST、CDT、MST、MDT、PST、PDT,| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| West | A | B | C | D | E | F | G | H | I | K | L | M |
| Eest | N | O | P | Q | R | S | T | U | V | W | X | Y |
生成符合要求的时间字符串比较简单:
import time
time.strftime('%a, %d %b %Y %H:%M:%S %z')
# 'Tue, 10 Apr 2018 09:10:05 +0800'
但是由于这个灵活度比较大,解析起来最好借助专业的库(email.utils)来做这个事。
import time
import datetime
import email.utils
import pytz
# 解析 ############################################
date_str = 'Sun, 20 Jun 2018 00:47:04 -0700 (PDT)'
email.utils.parsedate_to_datetime(date_str)
# datetime.datetime(2018, 6, 20, 0, 47, 4, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))
email.utils.parsedate_tz(date_str)
(2018, 6, 20, 0, 47, 4, 0, 1, -1, -25200)
# 生成 ############################################
# email.utils.formatdate(timeval=None, localtime=False, usegmt=False)
email.utils.formatdate()
# 'Tue, 10 Apr 2018 09:10:41 -0000'
# email.utils.format_datetime(dt, usegmt=False)
dt = datetime.datetime.now()
email.utils.format_datetime(dt)
# 'Tue, 10 Apr 2018 09:16:43 -0000'
tz = pytz.timezone('Asia/Shanghai') # <DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>
dt = datetime.datetime(2018, 4, 10, 9, 10, 0, tzinfo=tz)
# datetime.datetime(2018, 4, 10, 9, 10, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)
email.utils.format_datetime(dt)
# 'Tue, 10 Apr 2018 09:10:00 +0806'
规范定义比较复杂,甚至支持注释。
我简化一下(去掉注释,去掉双引号,去掉 [IPv4] / [IPv6] / 主机名 做域):
域内部分@域域内部分:
长度不超过 64
ASCII 标点符号(19)
!#$%&'*+-/=?^_`{|}~
可以加入点号(.)隔开,不放首尾,不连续出现
域名
每一级域名 1 - 63 个字符,总长度不超过 253
这个限制和 DNS 报文设计有关
国际化域名转换成 Punycode 之后也必须遵守这个约定
-)实际上的邮件地址会更加简单:
.-_.-_)不可连续出现+taobao,订阅开发者头条时 +toutiao,相关邮件就方便搜索归类。以规范为准,参考真实场景下的实践:
域内部分:
/[a-z0-9]+([.-_#][a-z0-9]+)+/;
域名部分:
/[a-z0-9]+(-[a-z0-9]+)?(\.[a-z0-9]+(-[a-z0-9]+)?)+/;
汇总在一起就是:
/^[a-z0-9]+([.-_#][a-z0-9]+)+@[a-z0-9]+(-[a-z0-9]+)?(\.[a-z0-9]+(-[a-z0-9]+)?)+$/;
相关文章:
在 Linux 内核的网络设备管理层,虚拟设备和物理设备是同等地位。
Neutron 网络模式:
| 类别 | 英语名称 | 简写 | 说明 |
|---|---|---|---|
| 名词 | noun | n | - |
| 动词 | verb | v | - |
| 形容词 | adjective | adj | - |
| 副词 | adverb | ad/adv | - |
| 介词 | preposition | perp | in on at |
| 连词 | conjunction | conj | and or but if |
| 代词 | pronoun | pron | I you he she |
| 数词 | numeral | num | one two three |
| 冠词 | article | art | the a an |
| 感叹词 | interjection | interj | oh hey |
在 HTML 开发中,alt 是图片标签 <img> 的一个属性,表示图片的“替代文本(Alternative Text)”。
当图片无法正常加载时,alt 属性中的文本将作为替代内容显示出来。
此外,alt 属性还有助于提高网页的可访问性,因为屏幕阅读器会通过 alt 文本来帮助视障用户理解图片的内容。
<img src="example.jpg" alt="风景优美的湖泊" />
当图片 example.jpg 无法加载时,用户会看到替代文本“风景优美的湖泊”。
就像上面说过的,alt 主要作用是提升可访问性
由于网络文档,图片加载失败时,alt 属性可以传达一些信息(这个意义真的重要么?可以协助前端排查问题)。
对于视障用户,屏幕阅读器会读取图片的 alt 属性,让他们知道图片的含义。这是建设无障碍网页的基础之一。
SEO(搜索引擎优化) 帮助搜索引擎理解网页内容
简单明了(有利于解读页面内容(堆砌关键词真的有用吗?))
不要对页面内容累赘描述
重力引起的标准加速度(或自由落体的标准加速度),有时缩写为标准重力,通常用 $ɡ_0$ 或 $ɡ_n$ 表示,是物体在地球表面附近真空中的标称重力加速度。
标准定义为 $9.80665 m/s^2$(约 $32.17405 ft/s^2$)。
该值由第三届国际度量衡大会确定,用于将物体的标准重量定义为其质量和标称加速度的乘积。
地球表面附近物体的加速度是由于重力和地球自转的离心加速度的综合作用(但后者足够小,在大多数情况下可以忽略不计);
两极的总重力(视重力)比赤道高约百分之二。
参见:标准重力
https://en.wikipedia.org/wiki/Standard_gravity