简介
发邮件前我们需要了解的是邮件是怎么一个形式去发送到对方手上的, 通俗点来说就是你写好一封信, 然后装进信封, 写上地址, 贴上邮票, 然后就近找个邮局, 把信仍进去, 其他的就不关心了, 只是关心时间, 而电子邮件不像日常发送邮件的按天算, 时间都是按
秒算的.
电子邮件的发送流程:
1, 你使用某款电子邮件软件 MUA:mail user agent -- 邮件用户代理, 填写完成点击发送
2, 在你点击发送的时候电子邮件软件发出去, 到 MTA:mail transfer agent-- 邮件传输代理, 即 email 服务提供商, 如网易等
3,MTA-- 邮箱传输代理会把邮箱投递到邮件的最终目的地 MDA:mail delivery agent--- 邮箱投递服务
4,email 到达 MDA 后就会放在某个服务器上, 存在数据库里, 收件人必须通过 MUA 从 MDA 中把邮箱放到自己电脑上
发件人 -> MUA -> MTA -> MTA -> 若干个 MTA -> MDA<-MUA<- 收件人
python 发邮件需要掌握两个模块的用法, smtplib 和 email, 这俩模块是 python 自带的, 只需 import 即可使用. smtplib 模块主要负责发送邮件, email 模块主要负责构造邮件.
smtplib 模块主要负责发送邮件: 是一个发送邮件的动作, 连接邮箱服务器, 登录邮箱, 发送邮件(有发件人, 收信人, 邮件内容).
email 模块主要负责构造邮件: 指的是邮箱页面显示的一些构造, 如发件人, 收件人, 主题, 正文, 附件等.
1.smtplib 模块
smtplib 使用较为简单. 以下是最基本的语法.
导入及使用方法如下:
- import smtplib
- smtp = smtplib.SMTP()
- smtp.connect('smtp.163.com,25')
- smtp.login(username, password)
- smtp.sendmail(sender, receiver, msg.as_string())
- smtp.quit()
说明:
smtplib.SMTP(): 实例化 SMTP()
connect(host,port):
host: 指定连接的邮箱服务器. 常用邮箱的 smtp 服务器地址如下:
新浪邮箱: smtp.sina.com, 新浪 VIP:smtp.vip.sina.com, 搜狐邮箱: smtp.sohu.com,126 邮箱: smtp.126.com,139 邮箱: smtp.139.com,163 网易邮箱: smtp.163.com.
port: 指定连接服务器的端口号, 默认为 25.
login(user,password):
user: 登录邮箱的用户名.
password: 登录邮箱的密码, 像笔者用的是网易邮箱, 网易邮箱一般是网页版, 需要用到客户端密码, 需要在网页版的网易邮箱中设置授权码, 该授权码即为客户端密码.
sendmail(from_addr,to_addrs,msg,...):
from_addr: 邮件发送者地址
to_addrs: 邮件接收者地址. 发送邮件, 多人时用 list, 字符串列表 ['接收地址 1','接收地址 2','接收地址 3',...] 或'接收地址'
msg: 发送消息: 邮件内容. 邮件正文是一个 str, 一般是 msg.as_string():as_string()是将 msg(MIMEText 对象或者 MIMEMultipart 对象)变为 str.
quit(): 退出关闭邮箱, 用于结束 SMTP 会话.
2.email 模块
email 模块下有 mime 包, mime 英文全称为 "Multipurpose Internet Mail Extensions", 即多用途互联网邮件扩展, 是目前互联网电子邮件普遍遵循的邮件技术规范.
该 mime 包下常用的有三个模块: text,image,multpart.
导入方法如下:
- from email.mime.multipart import MIMEMultipart
- from email.mime.text import MIMEText
- from email.mime.image import MIMEImage
构造一个邮件对象就是一个 Message 对象, 如果构造一个 MIMEText 对象, 就表示一个文本邮件对象, 如果构造一个 MIMEImage 对象, 就表示一个作为附件的图片, 要把多个对象组合起来, 就用 MIMEMultipart 对象, 而 MIMEBase 可以表示任何对象. 它们的继承关系如下:
- Message
- +- MIMEBase
- +- MIMEMultipart
- +- MIMENonMultipart
- +- MIMEMessage
- +- MIMEText
- +- MIMEImage
2.1 text 说明
邮件发送程序为了防止有些邮件阅读软件不能显示处理 html 格式的数据, 通常都会用两类型分别为 "text/plain" 和 "text/html"
构造 MIMEText 对象时, 第一个参数是邮件正文, 第二个参数是 MIME 的 subtype, 最后一定要用 utf-8 编码保证多语言兼容性.
2.1.1 添加普通文本
方法很简单, 在构造 MIMEText 对象时, 把 TEXT 字符串传进去, 再把第二个参数 plain 和第三个参数编码传进去即可. plain 表示纯文本 , 后面的则是编译, 保证多语言兼容
- text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.cnblogs.com/du-hong"
- text_plain = MIMEText(text,'plain', 'utf-8')
查看 MIMEText 属性: 可以观察到 MIMEText,MIMEImage 和 MIMEMultipart 的属性都一样.
- print dir(text_plain)
- ['__contains__', '__delitem__', '__doc__', '__getitem__', '__init__', '__len__', '__module__', '__setitem__', '__str__', '_charset', '_default_type', '_get_params_preserve', '_headers', '_payload', '_unixfrom', 'add_header', 'as_string', 'attach', 'defects', 'del_param', 'epilogue', 'get', 'get_all', 'get_boundary', 'get_charset', 'get_charsets', 'get_content_charset', 'get_content_maintype', 'get_content_subtype', 'get_content_type', 'get_default_type', 'get_filename', 'get_param', 'get_params', 'get_payload', 'get_unixfrom', 'has_key', 'is_multipart', 'items', 'keys', 'preamble', 'replace_header', 'set_boundary', 'set_charset', 'set_default_type', 'set_param', 'set_payload', 'set_type', 'set_unixfrom', 'values', 'walk']
2.1.2 添加超文本(HTML)
方法也很简单, 在构造 MIMEText 对象时, 把 HTML 字符串传进去, 再把第二个参数由 plain 变为 HTML 就可以了
HTML = """
<html>
<body>
<p>
Here is the <a href="https://www.cnblogs.com/du-hong">link</a> you wanted.
</p>
</body>
</html>
""" text_html = MIMEText(HTML,'html','utf-8')
2.1.3 添加附件
首先要创建 MIMEMultipart()实例, 然后构造附件, 如果有多个附件, 可依次构造, 最后利用 smtplib.smtp 发送
- sendfile=open(r'D:\pythontest\text.txt','rb').read()
- text_att = MIMEText(sendfile, 'base64', 'utf-8')
- text_att["Content-Type"] = 'application/octet-stream'
- text_att["Content-Disposition"] = 'attachment; filename=" 显示的名字. txt"'
2.2 image 说明
添加图片:
- sendimagefile=open(r'D:\pythontest\testimage.png','rb').read()
- image = MIMEImage(sendimagefile)
- image.add_header('Content-ID','<image1>')
查看 MIMEImage 属性:
- print dir(image)
- ['__contains__', '__delitem__', '__doc__', '__getitem__', '__init__', '__len__', '__module__', '__setitem__', '__str__', '_charset', '_default_type', '_get_params_preserve', '_headers', '_payload', '_unixfrom', 'add_header', 'as_string', 'attach', 'defects', 'del_param', 'epilogue', 'get', 'get_all', 'get_boundary', 'get_charset', 'get_charsets', 'get_content_charset', 'get_content_maintype', 'get_content_subtype', 'get_content_type', 'get_default_type', 'get_filename', 'get_param', 'get_params', 'get_payload', 'get_unixfrom', 'has_key', 'is_multipart', 'items', 'keys', 'preamble', 'replace_header', 'set_boundary', 'set_charset', 'set_default_type', 'set_param', 'set_payload', 'set_type', 'set_unixfrom', 'values', 'walk']
2.3 multpart 说明
常见的 multipart 类型有三种: multipart/alternative, multipart/related 和 multipart/mixed.
邮件类型为 "multipart/alternative" 的邮件包括纯文本正文 (text/plain) 和超文本正文(text/HTML).
邮件类型为 "multipart/related" 的邮件正文中包括图片, 声音等内嵌资源.
邮件类型为 "multipart/mixed" 的邮件包含附件. 向上兼容, 如果一个邮件有纯文本正文, 超文本正文, 内嵌资源, 附件, 则选择 mixed 类型.
msg = MIMEMultipart('mixed')
我们必须把 Subject,From,To,Date 添加到 MIMEText 对象或者 MIMEMultipart 对象中, 邮件中才会显示主题, 发件人, 收件人, 时间(若无时间, 就默认一般为当前时间, 该值一般不设置).
- msg = MIMEMultipart('mixed')
- msg['Subject'] = 'Python email test'
- msg['From'] = 'XXX@163.com <XXX@163.com>'
- msg['To'] = 'XXX@126.com'
- msg['Date']='2019-5-10'
查看 MIMEMultipart 属性:
- msg = MIMEMultipart('mixed')
- print dir(msg)
结果:
['__contains__', '__delitem__', '__doc__', '__getitem__', '__init__', '__len__', '__module__', '__setitem__', '__str__', '_charset', '_default_type', '_get_params_preserve', '_headers', '_payload', '_unixfrom', 'add_header', 'as_string', 'attach', 'defects', 'del_param', 'epilogue', 'get', 'get_all', 'get_boundary', 'get_charset', 'get_charsets', 'get_content_charset', 'get_content_maintype', 'get_content_subtype', 'get_content_type', 'get_default_type', 'get_filename', 'get_param', 'get_params', 'get_payload', 'get_unixfrom', 'has_key', 'is_multipart', 'items', 'keys', 'preamble', 'replace_header', 'set_boundary', 'set_charset', 'set_default_type', 'set_param', 'set_payload', 'set_type', 'set_unixfrom', 'values', 'walk']
说明:
msg.add_header(_name,_value,**_params): 添加邮件头字段.
msg.as_string(): 是将 msg(MIMEText 对象或者 MIMEMultipart 对象)变为 str, 如果只有一个 HTML 超文本正文或者 plain 普通文本正文的话, 一般 msg 的类型可以是 MIMEText; 如果是多个的话, 就都添加到 MIMEMultipart,msg 类型就变为 MIMEMultipart.
msg.attach(MIMEText 对象或 MIMEImage 对象): 将 MIMEText 对象或 MIMEImage 对象添加到 MIMEMultipart 对象中. MIMEMultipart 对象代表邮件本身, MIMEText 对象或 MIMEImage 对象代表邮件正文.
以上的构造的文本, 超文本, 附件, 图片都何以添加到 MIMEMultipart('mixed')中:
- msg.attach(text_plain)
- msg.attach(text_html)
- msg.attach(text_att)
- msg.attach(image)
3. 文字, HTML, 图片, 附件实现实例
3.1 实例
3.2 运行结果
3.3 参考代码
- # coding=utf-8
- #1. 先设置编码, utf-8 可支持中英文, 如上, 一般放在第一行
- #2. 注释: 包括记录创建时间, 创建人, 项目名称.
- '''
- Created on 2019-5-10
- @author: 北京 - 宏哥
- Project: 学习和使用邮箱发文字, html, 图片, 附件实现实例邮件
- '''
- #3. 导入模块
- import smtplib
- from email.mime.multipart import MIMEMultipart
- from email.mime.text import MIMEText
- from email.mime.image import MIMEImage
- from email.header import Header
- # 设置 smtplib 所需的参数
- # 下面的发件人, 收件人是用于邮件传输的.
- smtpserver = 'smtp.mxhichina.com'
- username = 'noreply@xxx.cn'
- password = 'xxxx@@xx3'
- sender = 'noreply@xxx.cn'
- # 收件人为一个收件人
- # receiver='XXX@126.com'
- # 收件人为多个收件人
- receiver = ["1918991791@qq.com","2014816656@qq.com","hongge@xxx.cn"]
- subject = 'Python email test'
- # 通过 Header 对象编码的文本, 包含 utf-8 编码信息和 Base64 编码信息. 以下中文名测试 ok
- # subject = '中文标题'
- # subject=Header(subject, 'utf-8').encode()
- # 构造邮件对象 MIMEMultipart 对象
- # 下面的主题, 发件人, 收件人, 日期是显示在邮件页面上的.
- msg = MIMEMultipart('mixed')
- msg['Subject'] = subject
- msg['From'] = '北京 - 宏哥 < noreply@xxx.cn>'
- # msg['To'] = "1918991791@qq.com","2014816656@qq.com","hongge@xxx.cn"
- # 收件人为多个收件人, 通过 join 将列表转换为以; 为间隔的字符串
- msg['To'] = ";".join(receiver)
- # msg['Date']='2019-5-10'
- # 构造文字内容
- text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.cnblogs.com/du-hong"
- text_plain = MIMEText(text, 'plain', 'utf-8')
- msg.attach(text_plain)
- # 构造图片链接
- sendimagefile = open(r'E:\pythontest\testimage.png', 'rb').read()
- image = MIMEImage(sendimagefile)
- image.add_header('Content-ID', '<image1>')
- image["Content-Disposition"] = 'attachment; filename="honggeimage.png"'
- msg.attach(image)
- # 构造 HTML
- # 发送正文中的图片: 由于包含未被许可的信息, 网易邮箱定义为垃圾邮件, 报 554 DT:SPM :<p><img src="cid:image1"></p>
- HTML = """
- <html>
- <head></head>
- <body>
- <p>Hi!<br>
- How are you?<br>
- Here is the <a href="https://www.cnblogs.com/du-hong/">link 北京 - 宏哥</a> you wanted.<br>
- </p>
- </body>
- </html>
- """ text_html = MIMEText(HTML,'html','utf-8')
- text_html["Content-Disposition"] = 'attachment; filename="texthtml.html"'
- msg.attach(text_html)
- # 构造附件
- sendfile = open(r'E:\pythontest\text.txt', 'rb').read()
- text_att = MIMEText(sendfile, 'base64', 'utf-8')
- text_att["Content-Type"] = 'application/octet-stream'
- # 以下附件可以重命名成 aaa.txt
- # text_att["Content-Disposition"] = 'attachment; filename="aaa.txt"'
- # 另一种实现方式
- text_att.add_header('Content-Disposition', 'attachment', filename='hongge.txt')
- # 以下中文测试不 ok
- # text_att["Content-Disposition"] = u'attachment; filename=" 中文附件. txt"'.decode('utf-8')
- msg.attach(text_att)
- # 发送邮件
- smtp = smtplib.SMTP()
- smtp.connect('smtp.mxhichina.com')
- # 我们用 set_debuglevel(1)就可以打印出和 SMTP 服务器交互的所有信息.
- # smtp.set_debuglevel(1)
- smtp.login(username, password)
- smtp.sendmail(sender, receiver, msg.as_string())
- smtp.quit()
说明:
对有些 header 要特别留意, 服务器会针对这些 header 做检查
User-Agent : 有些服务器或 Proxy 会通过该值来判断是否是浏览器发出的请求
Content-Type : 在使用 REST 接口时, 服务器会检查该值, 用来确定 HTTP Body 中的内容该怎样解析. 常见的取值有:
application/xml : 在 xml RPC, 如 RESTful/SOAP 调用时使用
application/JSON : 在 JSON RPC 调用时使用
application/x-www-form-urlencoded : 浏览器提交 web 表单时使用
在使用服务器提供的 RESTful 或 SOAP 服务时, Content-Type 设置错误会导致服务器拒绝服务
小结
好了, 哈哈, 到这里把 python 发邮件的十八辈祖宗都被我们挖出来了, 自己都感觉到有点缺德, 但是我们学习就是需要这种精神, 但是也不要过于钻牛角筋, 要适可而止. 想必小伙伴们对 python 发邮件有了更深刻的认识了, 以后遇到类似的问题,
往上套就可以了, 但是要注意方式和方法, 不要生搬硬套, 生搬硬套又会出现问题, 要灵活有技巧的套.
来源: https://www.cnblogs.com/du-hong/p/10832048.html