手把手教你用 Python 发送 Email

手把手教你用 Python 发送 Email


SMTP 是发送邮件的协议,Python 内置对 SMTP 的支持,可以发送纯文本邮件,HTML 邮件以及带附件的邮件。
Python 对 SMTP 支持主要有smtplibemail两个模块,email模块负责构造邮件,smtplib模块负责发送邮件。

这里,我们将由简到繁,逐步探索 Python 发送各种类型邮件的方法。

1. 发送简单的纯文本邮件

已经编辑好的源代码如下:

import smtplib
from email.header import Header
from email.mime.text import MIMEText

from_addr = 'xxxxxxx@qq.com'      # 发送邮件的邮箱地址
from_pass = 'xxxxxx'                 # 发送邮件的邮箱密码
from_user = 'from_nickname'          # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'          # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@vip.qq.com'    # 接收邮件的邮箱地址
to_user = 'to_nickname'              # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)
cc_addr = 'xxxxxxx@sina.com'        # 抄送邮件的邮箱地址
cc_user = 'sina'                     # 抄送邮件的邮箱用户昵称

msg = MIMEText('你好,\n这里是正文内容,您的第一封Python邮件。\n祝好,\n再见。', 'plain', 'utf-8') # 邮件的正文内容

from_nickname = Header(from_user, 'utf-8')
from_nickname.append('<'+from_addr+'>', 'ascii')
msg["From"] = from_nickname          #昵称用'utf-8'编码,地址用'ascii'编码
to_nickname = Header(to_user, 'utf-8')
to_nickname.append('<'+to_addr+'>', 'ascii')
msg["To"] = to_nickname              #昵称用'utf-8'编码,地址用'ascii'编码
cc_nickname = Header(cc_user, 'utf-8')
cc_nickname.append('<'+cc_addr+'>', 'ascii')
msg["CC"] = to_nickname              #昵称用'utf-8'编码,地址用'ascii'编码
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

server = smtplib.SMTP(from_server, 25)  # SMTP协议默认端口是25
server.set_debuglevel(1)                # 查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

其中,msg["From"]是发件人信息,msg["To"]是收件人信息,msg["CC"]是抄送人信息, msg['Subject']是邮件主题。使用set_debuglevel设置为 1 可以打印出程序和SMTP服务器交互的所有信息。login()方法用来登陆SMTP服务器,senmail()方法可以发送邮件。如果一切顺利,就可以在收件人的邮箱中收到我们刚发送的 Email。
FirstPyEmail.png
可能你的邮箱中收件人或发件人的昵称并不是你所设定的名字,那是因为你的邮箱通讯录里已经有了这个邮箱地址,因此收信软件在收到邮件后自动替换为你通讯录里保存的名字。

2. 简单的制作 msg 头的方法

上面的那个例子在制作 msg 头时,采用了 utf-8 编码的收件人昵称和 ascii 编码的收信地址。这里我们可以才用一个更加简单的方法,直接将两者字符串相加。

msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'

这样就方便多了,代码也更加清爽。之所以保留前面的方法是以防这里的方法在某些情况遇到错误,虽然现在还没有遇见。完整的代码如下:

from email.header import Header
from email.mime.text import MIMEText
import smtplib

from_addr = 'xxxxxxx@qq.com'    # 发送邮件的邮箱地址
from_pass = 'xxxxxxx'              # 发送邮件的邮箱密码
from_user = 'from_nickname'        # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'        # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@sina.com'      # 接收邮件的邮箱地址
to_user = 'to_nickname'            # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

msg = MIMEText('你好,\n这里是正文内容,您的第一封Python邮件。\n祝好,\n再见。', 'plain', 'utf-8')     # 邮件的正文内容
msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

server = smtplib.SMTP(from_server, 25)  # SMTP协议默认端口是25
server.set_debuglevel(1)                # 查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

3. 发送HTML邮件

普通的文本文件表现力较差,HTML 语言可以表现出更丰富的外观特性。发送 HTML 邮件就是将普通的文本内容替换为 HTML 语言,并将MIMEText中的属性更改为html。将内容代码改变如下:

msg = MIMEText('''
<meta charset="utf-8" /><h1>我是标题</h1><hr/><p>我是正文,<em>我是斜体</em>,
<strong>我是粗体</strong></p>
''', 'html', 'utf-8')

完整的代码如下:

from email.header import Header
from email.mime.text import MIMEText
import smtplib

from_addr = 'xxxxxxx@qq.com'      # 发送邮件的邮箱地址
from_pass = 'xxxxxxx'                # 发送邮件的邮箱密码
from_user = 'from_nickname'          # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'          # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@vip.qq.com'    # 接收邮件的邮箱地址
to_user = 'to_nickname'              # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

msg = MIMEText('''
<meta charset="utf-8" /><h1>我是标题</h1><hr/><p>我是正文,<em>我是斜体</em>,
<strong>我是粗体</strong></p>
''', 'html', 'utf-8')

msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

server = smtplib.SMTP(from_server, 25)  # SMTP协议默认端口是25
server.set_debuglevel(1)                # 查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

qq 邮箱收件箱内容如下图:
HTMLEmailqq.png
然而有些有些不支持完全的 HTML 形式的显示,比如 sina 邮箱:
HTMLEmailsina.png

毕竟 HTML 语言格式比较复杂,可以在这个工具网站上将 Markdown 语法的排版转为 HTML 语言,这样可以使我们更加专注于内容的书写。

4. 在正文中添加图片

可以直接在上面的 HTML 内容中直接嵌入网络图片。我测试的效果,无论 qq 邮箱、sina 邮箱,还是收信客户端都可以正常显示,但是按照网上的添加附件引用<img src="cid:0">的方法,并不能成功。因此在这里重点推荐网络图片的方法。具体代码如下:

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

from_addr = 'xxxxxxx@qq.com'      # 发送邮件的邮箱地址
from_pass = 'xxxxxxx'                # 发送邮件的邮箱密码
from_user = 'from_nickname'          # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'          # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@vip.qq.com'    # 接收邮件的邮箱地址
to_user = 'to_nickname'              # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

msg = MIMEMultipart()
# 正常加载图片。网络上的cid引用失败。建议不要加载本地图片。
part = MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br>'+
                '<img src="https://www.python.org/static/img/python-logo.png" /></img>','html','utf-8')
msg.attach(part)

# 邮件的正文内容
msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

server = smtplib.SMTP(from_server, 25)  # SMTP协议默认端口是25
server.set_debuglevel(1)                # 查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

收件箱内容如图:
HtmlPic.png

5. 尝试发给多人邮件

如果我们将to_addr中的邮箱地址变为一个list,就可以将邮件发送给包含在list中的所有地址。更改代码如下:

to_addr = ['xxxxxxx@vip.qq.com','xxxxxxx@sina.com']    # 接收邮件的邮箱地址

再次运行,我们的两个收件箱均能收到邮件,但是当我们查看 sina 邮箱时,会出现下图的内容:
MassMailSina.png

可以看到,收件人的地址并不是我的 sina 邮箱,但仍然收到邮件,这是因为我们看到的收发件信息是包含在邮件内容中的,并不是发送的地址。因此由于之前的程序并未对不同的地址进行收件人头信息更改,所以出现了这样的结果。
如果我们想针对不同的邮件地址发送不同的收件人信息,可以采用字典加for循环的方式实现。代码如下:

from email.header import Header
from email.mime.text import MIMEText
import smtplib
import re

def getMailName(mail_addr):
    regex = re.compile(r"(^[-\w.]{0,64})@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$", re.IGNORECASE)
    # 邮件地址的正则表达式提取
    mail_name = re.search(regex, mail_addr).group(1)
    return mail_name

def massmail(address, name):
   from_addr = 'xxxxxxx@qq.com'    # 发送邮件的邮箱地址
   from_pass = 'xxxxxxx'              # 发送邮件的邮箱密码
   from_user = 'from_nickname'        # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
   from_server = 'smtp.qq.com'        # 发送邮件的邮件服务器
   to_addr = address                  # 接收邮件的邮箱地址
   if name is None:                   # 如果收件人昵称不存在,则默认为邮箱名
      to_user = getMailName(address)
   else:
      to_user = name

   msg = MIMEText('你好,\n这里是群发邮件的正文内容。', 'plain', 'utf-8')  # 邮件的正文内容
   from_nickname = Header(from_user, 'utf-8')
   from_nickname.append('<'+from_addr+'>', 'ascii')
   msg["From"] = from_nickname       #昵称用'utf-8'编码,地址用'ascii'编码
   to_nickname = Header(to_user, 'utf-8')
   to_nickname.append('<'+to_addr+'>', 'ascii')
   msg["To"] = to_nickname           #昵称用'utf-8'编码,地址用'ascii'编码
   msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

   server = smtplib.SMTP(from_server, 25) # SMTP协议默认端口是25
   server.set_debuglevel(1)  #查看程序交互信息
   server.login(from_addr, from_pass)
   server.sendmail(from_addr, to_addr, msg.as_string())
   server.quit()

if __name__ == '__main__':
   addr_list = {'xxxxxxx@hotmail.com':None,'xxxxxxx@sina.com':'sina'}   # 收件人字典
   for (addr, name) in addr_list.items():
      massmail(addr, name)

这里我们使用了正则表达式来提取邮件地址中的用户名部分。
运行代码后,我们将分别在两个邮件账户中收到邮件。程序设定如果收件人名为None的话,自动默认为邮件地址中的用户名部分,因此最终结果如下图。
Masshotmail.png
Masssina.png

6. 尝试群发邮件

如果我们希望像普通群发邮件那样将所有收件人的地址都显示出来,将所有收件人的信息都加入到邮件内中,可以采用for循环的方式将收件人信息写入msg["To"]中,采用下面的代码:

from email.header import Header
from email.mime.text import MIMEText
import smtplib
import re

def getMailName(mail_addr):
    regex = re.compile(r"(^[-\w.]{0,64})@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$", re.IGNORECASE)
    # 邮件地址的正则表达式提取
    mail_name = re.search(regex, mail_addr).group(1)
    return mail_name

def groupmail(address_list):
   from_addr = 'xxxxxxx@qq.com'    # 发送邮件的邮箱地址
   from_pass = 'xxxxxxx'              # 发送邮件的邮箱密码
   from_user = 'from_nickname'        # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
   from_server = 'smtp.qq.com'        # 发送邮件的邮件服务器
   to_addr = []                       # 接收邮件的邮箱地址
   to_user = []                       # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

   msg = MIMEText('你好,\n这里是群发邮件的正文内容。', 'plain', 'utf-8')
   # from_nickname
   from_nickname = Header(from_user, 'utf-8')
   from_nickname.append('<'+from_addr+'>', 'ascii')
   msg["From"] = from_nickname       #昵称用'utf-8'编码,地址用'ascii'编码
   # to_nickname
   for (addr, name) in address_list.items():
      if name is None:                   # 如果收件人昵称不存在,则默认为邮箱名
         name = getMailName(addr)
      to_nickname = name+'<'+addr+'>'
      to_user.append(to_nickname)
      to_addr.append(addr)
   msg['To']=','.join(to_user)

   msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

   #发送邮件
   try:
      server = smtplib.SMTP(from_server, 25) # SMTP协议默认端口是25
      server.login(from_addr, from_pass)
      server.sendmail(from_addr, to_addr, msg.as_string())
      print('发送成功')
   except Exception as e:
      print(e)
   finally:
      server.quit()

if __name__ == '__main__':
   addr_list = {'xxxxxxx@hotmail.com':None,'xxxxxxx@sina.com':'sina',
                'xxxxxxx@vip.qq.com':'qq'}   # 收件人字典
   groupmail(addr_list)

收件箱内容如下图:
groupmail.png
细心的读者可以发现,只有改变了msg['To']的结构,才能在收件人列表中显示出来。这说明收件人的信息完全是文件内容,与实际发送地址本质上无关。
同时连接服务器使用了try...except...finally...语句,捕捉错误信息(如果有的话)。

7. 密送给联系人

如果想将群发的邮件发给相关联系人,如管理员等,但又不希望收件人和抄送人知道,就应该使用密送功能了。密送的收件人可以收到邮件,但地址不会显示在邮件中收件人和抄送人名单中。因此收件人和抄送人不会看到密送人。
功能的实现方法也很简单,其实在第 5 节已经实现了一次,当时收到了地址不在收件人列表中的邮件。前面已经说过,收件人信息和抄送人信息都是写在邮件内容中的,只要我们不添加密送人地址信息,就不会显示。同时将密送人地址添加到程序的收件地址列表中就可以了。代码如下:

from email.header import Header
from email.mime.text import MIMEText
import smtplib
import re

def getMailName(mail_addr):
    regex = re.compile(r"(^[-\w.]{0,64})@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$", re.IGNORECASE)
    # 邮件地址的正则表达式提取
    mail_name = re.search(regex, mail_addr).group(1)
    return mail_name

def groupmail(address_list, bcc_list):
    from_addr = 'xxxxxxx@qq.com'    # 发送邮件的邮箱地址
    from_pass = 'xxxxxxx'              # 发送邮件的邮箱密码
    from_user = 'from_nickname'        # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
    from_server = 'smtp.qq.com'        # 发送邮件的邮件服务器
    to_addr = []                       # 接收邮件的邮箱地址
    to_user = []                       # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

    msg = MIMEText('你好,\n这里是群发邮件的正文内容。', 'plain', 'utf-8')
    # from_nickname
    from_nickname = Header(from_user, 'utf-8')
    from_nickname.append('<'+from_addr+'>', 'ascii')
    msg["From"] = from_nickname            # 昵称用'utf-8'编码,地址用'ascii'编码
    # to_nickname
    for (addr, name) in address_list.items():
        if name is None:                   # 如果收件人昵称不存在,则默认为邮箱名
            name = getMailName(addr)
        to_nickname = name+'<'+addr+'>'
        to_user.append(to_nickname)
        to_addr.append(addr)
    msg['To']=','.join(to_user)

    msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

    #发送邮件
    try:
        server = smtplib.SMTP(from_server, 25)   # SMTP协议默认端口是25
        server.login(from_addr, from_pass)
        to_addr.extend(bcc_list)
        server.sendmail(from_addr, to_addr, msg.as_string())
        print('发送成功')
    except Exception as e:
        print(e)
    finally:
        server.quit()


if __name__ == '__main__':
    addr_list = {'xxxxxxx@hotmail.com':None,'xxxxxxx@sina.com':'sina'}   # 收件人字典
    bcc_list = ['xxxxxxx@vip.qq.com',]
    groupmail(addr_list, bcc_list)

发送后看一下密送人的收件箱:
bcclist.png
可以看到,在 qq 邮箱里收到了群发邮件,但是邮件地址不在收件人名单中。

8. 发送带附件的邮件

Python 的 email 函数将docxpptxxlsxjpgpdfmp3zip等格式的文件以附件的形式添加到文件中。
代码如下:

from email.header import Header
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import smtplib
import os

from_addr = 'xxxxxxx@qq.com'    # 发送邮件的邮箱地址
from_pass = 'xxxxxxx'              # 发送邮件的邮箱密码
from_user = 'from_nickname'        # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'        # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@vip.qq.com'  # 接收邮件的邮箱地址
to_user = 'to_nickname'            # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

msg = MIMEMultipart()
part = MIMEText('你好,\n这里是一封包含附件的邮件。', 'plain', 'utf-8')
msg.attach(part)

# 英文,数字名文件所有平台均成功;中文QQ邮箱成功,客户端,sina失败
# 建议:最好命名为英文和数字组合
filePath = 'C:\\files\\stock.pdf'
baseName = os.path.basename(filePath)
att = MIMEApplication(open(filePath,'rb').read())
att.add_header('Content-Disposition', 'attachment', filename=baseName)
msg.attach(att)

filePath = 'C:\\files\\run.jpg'
baseName = os.path.basename(filePath)
att = MIMEApplication(open(filePath,'rb').read())
att.add_header('Content-Disposition', 'attachment', filename=baseName)
msg.attach(att)

# 邮件的正文内容
msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

server = smtplib.SMTP(from_server, 25) # SMTP协议默认端口是25
server.set_debuglevel(1)  #查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

收件箱内容如下:
attrenqq.png
附件名为中文的结果:
attrcnqq.png
qq 邮箱对中文是支持的比较好的,sina 邮箱中文附件名会出现错误。因此建议尽量采用英文和数字命名。
attrcnsina.png

9. 同时支持 HTML 和 Plain 格式

一些老旧的邮箱不支持 HTML 格式的邮件,因此有必要照顾到这样的群体(但我想现在基本没有了吧)。这部分代码主要是需要将MIMEMultipart设置为'alternative',然后分别在MIMEText中写出对于格式的内容。收信方会自动判断。其余的都一样。

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

from_addr = 'xxxxxxx@qq.com'      # 发送邮件的邮箱地址
from_pass = 'xxxxxxx'                # 发送邮件的邮箱密码
from_user = 'from_nickname'          # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'          # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@sina.com'        # 接收邮件的邮箱地址
to_user = 'to_nickname'              # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

msg = MIMEMultipart('alternative')   # 指定类型为alternative

msg.attach(MIMEText('这里是正文内容', 'plain', 'utf-8'))
msg.attach(MIMEText('<html><body><h1>这里是正文内容</h1></body></html>', 'html', 'utf-8'))
# 正常发送msg对象...

# 邮件的正文内容
msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

server = smtplib.SMTP(from_server, 25) # SMTP协议默认端口是25
server.set_debuglevel(1)  #查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

这里可以利用这项功能为邮件写摘要,其中 plain 内容会出现在邮件摘要处。html 内容会显示在正文中。
plainhtml

10. 加密传输

之前的明文传输可能会在网络上被监听,采用加密传输会更安全一些。同时一些邮件服务商如 Gmail 提供的 SMTP 服务必须要加密传输。这里以 qq 邮箱为例设置加密传输。

from email.header import Header
from email.mime.text import MIMEText
import smtplib

from_addr = 'xxxxxxx@qq.com'      # 发送邮件的邮箱地址
from_pass = 'xxxxxxx'                # 发送邮件的邮箱密码
from_user = 'from_nickname'          # 发送邮件的邮箱用户昵称(显示在邮件中的发件人)
from_server = 'smtp.qq.com'          # 发送邮件的邮件服务器
to_addr = 'xxxxxxx@sina.com'         # 接收邮件的邮箱地址
to_user = 'to_nickname'              # 接收邮件的邮箱用户昵称(显示在邮件中的收件人)

msg = MIMEText('你好,\n这里是正文内容,您的第一封Python邮件。\n祝好,\n再见。', 'plain', 'utf-8')
# 邮件的正文内容
msg["From"] = from_user+'<'+from_addr+'>'
msg["To"] = to_user+'<'+to_addr+'>'
msg['Subject'] = Header('这里是主题', 'utf-8')  # 邮件的主题

smtp_server = 'smtp.qq.com'
smtp_port = 587
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
# 剩下的代码和前面的一模一样:
server.set_debuglevel(1)  #查看程序交互信息
server.login(from_addr, from_pass)
server.sendmail(from_addr, to_addr, msg.as_string())
server.quit()

SMTP 端口为 587。建立连接后创建starttls()方法,后面的代码和前面的发送邮件代码完全一样。

小结

以上内容基本囊括了各种邮件类型的发送方法,重点就是设置好邮件头。

构造一个邮件对象就是一个msg对象,如果构造一个MIMEText对象,就表示一个文本邮件对象,如果构造一个MIMEImage对象,就表示一个作为附件的图片,要把多个对象组合起来,就用MIMEMultipart对象,而MIMEBase可以表示任何对象。它们的继承关系如下:

  • Message
    • MIMEBase
      • MIMEMultipart
      • MIMENonMultipart
        • MIMEMessage
        • MIMEText
        • MIMEImage

PS:刚开始是用 sina 邮箱做测试的,结果还没开始怎么做账户就被限制发邮件了。后来一直用 qq 邮箱,频率比 sina 邮箱高多了,从来没有被限制。qq 邮箱是我用过的最好的免费邮箱。最后,贴两张做测试的 qq 邮箱截图收件箱,感谢腾讯的支持。
pyemailhardwork
pyemailhardworksend


参考资料:
[1] SMTP发送邮件 - 廖雪峰的官方网站
[2] python 七种邮件内容发送方法实例
[3] python发送带附件邮件详解

[TOC]