看了廖雪峰老师的教程:
一封电子邮件的旅程就是
发件人 -> MUA -> MTA -> MTA -> 若干个 MTA -> MDA <- MUA <- 收件人
有了上述基本概念, 要编写程序来发送和接收邮件, 本质上就是:
编写 MUA 把邮件发到 MTA;
编写 MUA 从 MDA 上收邮件.
发邮件时, MUA 和 MTA 使用的协议就是 SMTP:Simple Mail Transfer Protocol, 后面的 MTA 到另一个 MTA 也是用 SMTP 协议.
收邮件时, MUA 和 MDA 使用的协议有两种: POP:Post Office Protocol, 目前版本是 3, 俗称 POP3;IMAP:Internet Message Access Protocol, 目前版本是 4, 优点是不但能取邮件, 还可以直接操作 MDA 上存储的邮件, 比如从收件箱移到垃圾箱, 等等.
邮件客户端软件在发邮件时, 会让你先配置 SMTP 服务器, 也就是你要发到哪个 MTA 上. 假设你正在使用 163 的邮箱, 你就不能直接发到新浪的 MTA 上, 因为它只服务新浪的用户, 所以, 你得填 163 提供的 SMTP 服务器地址: smtp.163.com, 为了证明你是 163 的用户, SMTP 服务器还要求你填写邮箱地址和邮箱口令, 这样, MUA 才能正常地把 Email 通过 SMTP 协议发送到 MTA.
类似的, 从 MDA 收邮件时, MDA 服务器也要求验证你的邮箱口令, 确保不会有人冒充你收取你的邮件, 所以, Outlook 之类的邮件客户端会要求你填写 POP3 或 IMAP 服务器地址, 邮箱地址和口令, 这样, MUA 才能顺利地通过 POP 或 IMAP 协议从 MDA 取到邮件.
最后特别注意, 目前大多数邮件服务商都需要手动打开 SMTP 发信和 POP 收信的功能, 否则只允许在网页登录
SMTP 是发送邮件的协议, Python 内置对 SMTP 的支持, 可以发送纯文本邮件, html 邮件以及带附件的邮件.
Python 对 SMTP 支持有 smtplib 和 email 两个模块, email 负责构造邮件, smtplib 负责发送邮件.
- from email import encoders
- from email.header import Header
- from email.mime.text import MIMEText
- from email.utils import parseaddr, formataddr
- import smtplib
- def _format_addr(s):
- name, addr = parseaddr(s)
- return formataddr((Header(name, 'utf-8').encode(), addr))
- from_addr = input('From:')
- password = input('Password:')
- to_addr = input('To:')
- smtp_server = input('SMTP server:')
- msg = MIMEText('hello, send by Python...', 'plain', 'utf-8')
- msg['From'] = _format_addr('Python 爱好者 <%s>' % from_addr)
- msg['To'] = _format_addr('管理员 <%s>' % to_addr)
- msg['Subject'] = Header('来自 SMTP 的问候......', 'utf-8').encode()
- server = smtplib.SMTP(smtp_server, 25)
- server.set_debuglevel(1)
- server.starttls()
- server.login(from_addr, password) server.sendmail(from_addr, [to_addr], msg.as_string()) server.quit()
- # 解析
- from email.header import Header
- from email.mime.text import MIMEText
- from email.utils import parseaddr, formataddr
- import smtplib
- # 我们编写了一个函数_format_addr() 来格式化一个邮件地址. 注意不能# 简单地传入 name <addr@example.com>,
- # 因为如果包含中文, 需要通过 Header 对象进行编码.
- # def _format_addr(s):
- # name, addr = parseaddr(s)
- # return formataddr((Header(name, 'utf-8').encode(), addr))
- # from_addr = input('From:')
- from_addr = '1131769231@qq.com' # 发送者的邮箱
- # password = input('Password:')
- password = '授权码' # QQ 邮箱的授权码
- # to_addr = input('To:')
- to_addr = '3589307418@qq.com' # 接受者的邮箱
- #smtp_server = input('SMTP server:')
- smtp_server = 'smtp.qq.com' #服务器
- '''
注意到构造 MIMEText 对象时, 第一个参数就是邮件正文, 第二个参数是 MIME 的 subtype,
传入'plain'表示纯文本, 最终的 MIME 就是'text/plain'
最后一定要用 utf-8 编码保证多语言兼容性.
- '''msg = MIMEText('hello, send by Python...','plain','utf-8')
- msg['From'] = _format_addr('酱紫安 <%s>' % from_addr)
- msg['To'] = _format_addr('安酱紫 <%s>' % to_addr)
- msg['Subject'] = Header('主题 subject', 'utf-8').encode()
- server = smtplib.SMTP(smtp_server, 25)
- # set_debuglevel(1) 就可以打印出和 SMTP 服务器交互的所有信息
- server.set_debuglevel(1)
- '''
使用标准的 25 端口连接 SMTP 服务器时, 使用的是明文传输, 发送邮件的整个过程可能会被窃听.
要更安全地发送邮件, 可以加密 SMTP 会话, 实际上就是先创建 SSL 安全连接, 然后再使用 SMTP 协议发送邮件.
只需要在创建 SMTP 对象后, 立刻调用 starttls() 方法, 就创建了安全连接.
- '''
- server.starttls()
- # login() 方法用来登录 SMTP 服务器
- server.login(from_addr, password)
- # sendmail() 方法就是发邮件, 由于可以一次发给多个人, 所以传入一个 list
- # 邮件正文是一个 str,as_string() 把 MIMEText 对象变成 str
- server.sendmail(from_addr, [to_addr], msg.as_string())
- # 关闭
- server.quit()
py3 版本的默认 utf8, 会简单些,
- import smtplib
- from email.mime.text import MIMEText
- from email.utils import formataddr
- def email(email_list, content, subject="酱紫安"):
- msg = MIMEText(content, 'plain', 'utf-8')
- msg['From'] = formataddr(["酱",'1131769231@qq.com'])
- msg['Subject'] = subject
- # # SMTP 服务
- server = smtplib.SMTP("smtp.qq.com", 25)
- server.starttls()
- server.login("1131769231@qq.com", "授权码")
- server.sendmail('1131769231@qq.com', email_list, msg.as_string())
- server.quit()
发送 html:
- import smtplib
- from email.mime.text import MIMEText
- from email.utils import formataddr
- def email(email_list, content, subject="酱紫安"):
- msg = MIMEText(content, 'html', 'utf-8')
- msg['From'] = formataddr(["酱",'1131769231@qq.com'])
- msg['Subject'] = subject
- # # SMTP 服务
- server = smtplib.SMTP("smtp.qq.com", 25)
- server.starttls()
- server.login('1131769231@qq.com', '授权码')
- server.sendmail('1131769231@qq.com', email_list, msg.as_string())
- server.quit()
- #email_list = ['1343215444@qq.com', '3589307418@qq.com']
- email_list = ['3589307418@qq.com']
- content = '<h1>hello</h1> <a href="http://baidu.com"> 百度一下, 你就知道 </a>'
- email(email_list, content)
发送附件
如果 Email 中要加上附件怎么办? 带附件的邮件可以看做包含若干部分的邮件: 文本和各个附件本身, 所以, 可以构造一个 MIMEMultipart 对象代表邮件本身, 然后往里面加上一个 MIMEText 作为邮件正文, 再继续往里面加上表示附件的 MIMEBase 对象即可:
- import smtplib
- from email import encoders
- from email.mime.text import MIMEText
- from email.mime.multipart import MIMEMultipart
- from email.mime.base import MIMEBase
- from email.utils import formataddr
- def sendEmail(email_list, content, subject='你好'):
- msg = MIMEMultipart()
- msg['From'] = formataddr(["酱", '1131769231@qq.com'])
- msg['Subject'] = subject
- # 邮件正文是 MIMEText:
- msg.attach(MIMEText(content, 'html', 'utf-8'))
- # 添加附件就是加上一个 MIMEBase, 从本地读取一个图片:
- with open('1.png', 'rb') as f:
- # 设置附件的 MIME 和文件名, 这里是 png 类型:
- mime = MIMEBase('image', 'png', filename='test.png')
- # 加上必要的头信息:
- mime.add_header('Content-Disposition', 'attachment', filename='test.png')
- mime.add_header('Content-ID', '<0>')
- mime.add_header('X-Attachment-Id', '0')
- # 把附件的内容读进来:
- mime.set_payload(f.read())
- # 用 Base64 编码:
- encoders.encode_base64(mime)
- # 添加到 MIMEMultipart:
- msg.attach(mime)
- # # SMTP 服务
- server = smtplib.SMTP("smtp.qq.com", 25)
- server.starttls()
- server.login('1131769231@qq.com', '授权码')
- server.sendmail('1131769231@qq.com', email_list, msg.as_string())
- server.quit()
- email_list = ['3589307418@qq.com']
- content = '<h1>hello</h1> <a href="http://baidu.com"> 百度一下, 你就知道 </a>'
- sendEmail(email_list, content)
发送图片
如果要把一个图片嵌入到邮件正文中怎么做? 直接在 HTML 邮件中链接图片地址行不行? 答案是, 大部分邮件服务商都会自动屏蔽带有外链的图片, 因为不知道这些链接是否指向恶意网站.
要把图片嵌入到邮件正文中, 我们只需按照发送附件的方式, 先把邮件作为附件添加进去, 然后, 在 HTML 中通过引用 src="cid:0" 就可以把附件作为图片嵌入了. 如果有多个图片, 给它们依次编号, 然后引用不同的 cid:x 即可.
把上面代码加入 MIMEMultipart 的 MIMEText 从 plain 改为 html, 然后在适当的位置引用图片:
- msg.attach(MIMEText('<html><body><h1>Hello</h1>' +
- '<p><img src="cid:0"></p>' +
- '</body></html>', 'html', 'utf-8'))
同时支持 HTML 和 Plain 格式
如果我们发送 HTML 邮件, 收件人通过浏览器或者 Outlook 之类的软件是可以正常浏览邮件内容的, 但是, 如果收件人使用的设备太古老, 查看不了 HTML 邮件怎么办?
办法是在发送 HTML 的同时再附加一个纯文本, 如果收件人无法查看 HTML 格式的邮件, 就可以自动降级查看纯文本邮件.
利用 MIMEMultipart 就可以组合一个 HTML 和 Plain, 要注意指定 subtype 是 alternative:
- msg = MIMEMultipart('alternative')
- msg['From'] = ...
- msg['To'] = ...
- msg['Subject'] = ...
- msg.attach(MIMEText('hello', 'plain', 'utf-8'))
- msg.attach(MIMEText('<html><body><h1>Hello</h1></body></html>', 'html', 'utf-8'))
- # 正常发送 msg 对象...
加密 SMTP
使用标准的 25 端口连接 SMTP 服务器时, 使用的是明文传输, 发送邮件的整个过程可能会被窃听. 要更安全地发送邮件, 可以加密 SMTP 会话, 实际上就是先创建 SSL 安全连接, 然后再使用 SMTP 协议发送邮件.
某些邮件服务商, 例如 Gmail, 提供的 SMTP 服务必须要加密传输. 我们来看看如何通过 Gmail 提供的安全 SMTP 发送邮件.
必须知道, Gmail 的 SMTP 端口是 587, 因此, 修改代码如下:
- smtp_server = 'smtp.gmail.com'
- smtp_port = 587
- server = smtplib.SMTP(smtp_server, smtp_port)
- server.starttls()
- # 剩下的代码和前面的一模一样:
- server.set_debuglevel(1)
只需要在创建 SMTP 对象后, 立刻调用 starttls() 方法, 就创建了安全连接. 后面的代码和前面的发送邮件代码完全一样.
如果因为网络问题无法连接 Gmail 的 SMTP 服务器, 请相信我们的代码是没有问题的, 你需要对你的网络设置做必要的调整.
小结
使用 Python 的 smtplib 发送邮件十分简单, 只要掌握了各种邮件类型的构造方法, 正确设置好邮件头, 就可以顺利发出.
构造一个邮件对象就是一个 Messag 对象, 如果构造一个 MIMEText 对象, 就表示一个文本邮件对象, 如果构造一个 MIMEImage 对象, 就表示一个作为附件的图片, 要把多个对象组合起来, 就用 MIMEMultipart 对象, 而 MIMEBase 可以表示任何对象. 它们的继承关系如下:
- Message
- +- MIMEBase
- +- MIMEMultipart
- +- MIMENonMultipart
- +- MIMEMessage
- +- MIMEText
- +- MIMEImage
来源: https://www.cnblogs.com/jiangzijiang/p/8719649.html