smtplib 发送
流程
- 连接服务器 连接 SMTP 服务器,并使用用户名、密码登录服务器
- 创建邮件 创建
EmailMessage
对象,该对象代表邮件本身 - 发送邮件 调用代表与 SMTP 服务器连接的对象的
sendmail()
方法发送邮件
模块
smtplib
模块
email.message.EmailMessage
构建复杂邮件内容
set_content()
add_attachment(maintype,subtype,filename,cid=img)
发送经验
- 早期SMTP 服务器都采用普通的网络连接,因此默认端口是 25
- 现在绝大部分 SMTP 都是基于
SSL(Secure Socket Layer)
, 默认端口是465 - 有些公司的免费邮箱(比如 QQ 邮箱)默认是关闭了 SMTP
- 邮件太简单,比如没主题,邮箱服务商可能会被当成垃圾邮件拦截
- java成员变量有初始值,而局部变量没有初始值得,否则会导致编译不通过
发送示例
# py -3.7
import smtplib
import email.utils
from email.message import EmailMessage
smtp_server = 'smtp.qq.com'
from_addr = '2121995655@qq.com'
# qq授权码
password = 'xxxxxxxx'
to_addr = 'pardon110@163.com'
# 建立连接
# conn = smtplib.SMTP(smtp_server, 25)
conn = smtplib.SMTP_SSL(smtp_server, 465)
conn.set_debuglevel(1)
conn.login(from_addr, password)
# 创建邮件对象
msg = EmailMessage()
# 随机生成两个图片id
first_id, second_id = email.utils.make_msgid(), email.utils.make_msgid()
msg.set_content('<h2>邮件内容</h2><p>您好,这是一封来自Python的邮件' +
'<img src="cid:' + second_id[1:-1]+'"><p>')
msg['subject'] = '一封html邮件'
msg['from'] = '李刚<%s>' % from_addr
msg['to'] = '新用户<%s>' % to_addr
# 添加附件
with open('C:/Users/Public/Pictures/logo/coffee.png', 'rb') as f:
# 添加第一个附件
msg.add_attachment(f.read(), maintype='image',
subtype='jpeg', filename='coffee.png', cid=first_id)
with open('C:/Users/Public/Pictures/lyn/s009.jpg', 'rb') as f:
# 添加第二个附件
msg.add_attachment(f.read(), maintype='image',
subtype='jpeg', filename='test.jpeg', cid=second_id)
with open('E:/Manual/pdf/laravel-source-analysis.pdf', 'rb') as f:
# 添加第三个附件,邮件正文不需引用该附件,因此不指定cid
msg.add_attachment(f.read(), maintype='application',
subtype='pdf', filename='test.pdf',)
# 发送邮件
conn.sendmail(from_addr, [to_addr], msg.as_string())
conn.quit()
poplib 收件
模块类
poplib.POP3
poplib.POP3_SSL
pop3协议
- 请求,响应式交互协议
- POP3 的命令和响应数据都是基于 ASCII 文本的,并以 CR 和 LF(/r/n) 作为行结束符
- pop3协议命令
user name,pass string,stat,quit,list[msg_no],retr_msg_no,dele_msg_no,noop,rset
poplib收取流程
- 使用
poplib.POP3
或 poplib.POP3_SS
L 按 POP3 协议从服务器端下载邮件 - 使用
email.parser.Parser
或 email.parser.BytesParser
解析邮件内容,得到 EmailMessage
对象
小结
- 默认端口110,ssl端口995
email.parser.BytesParser
解析字节串格式的邮件数据email.parser.Parser
解析字符串格式的邮件数据- 若读取
EmailMessage
的各部分,则需要调用该对象的 walk()
方法,该方法返回一个可迭代对象 - 创建解析器时,*需要指定解析策略
policy=default
*否则,解析出来的对象就是 Message
,而不是新的 EmailMessage
- 读取 EmailMessage 的各部分,则需要调用该对象的 walk() 方法,该方法返回一个可迭代对象
收件示例
import poplib
import os.path
import mimetypes
from email.parser import BytesParser, Parser
from email.policy import default
# 输入邮件地址, 口令和POP3服务器地址
email = 'pardon110@qq.com'
# 授权码
password = 'pkvxxxxxxec'
pop3_server = 'pop.qq.com'
# 连接pop3服务器
# conn = poplib.POP3(pop3_server, 110)
conn = poplib.POP3_SSL(pop3_server, 995)
# 打开调试信息
conn.set_debuglevel(1)
# 可选:打印POP 3服务器的欢迎文字
print(conn.getwelcome().decode('utf-8'))
# 输入用户密码
conn.user(email)
conn.pass_(password)
# 获取邮件的统计信息,相当于发送pop3的stat命令
message_num, total_size = conn.stat()
print('邮件数:%s, 总大小:%s' % (message_num, total_size))
# 获取服务器上的邮件列表,相当于发送POP 3的list命令
# resp保存服务器的响应码
# mails列表保存每封邮件的编号、大小
resp, mails, octets = conn.list()
print(resp, mails)
# 获取指定邮件的内容(此处传入总长度,也就是获取最后一封邮件)
# 相当于发送POP 3的retr命令
# resp保存服务器的响应码
# data保存该邮件的内容
resp, data, octets = conn.retr(len(mails))
# 将data的所有数据(原本是一个字节列表)拼接在一起
msg_data = b'\r\n'.join(data)
# 将字符串内容解析成邮件,需指定解析策略
msg = BytesParser(policy=default).parsebytes(msg_data)
print(type(msg))
print('发件人:' + msg['from'])
print('收件人:' + msg['to'])
print('主题:' + msg['subject'])
print('第一个收件人名字:' + msg['to'].addresses[0].username)
print('第一个发件人名字:' + msg['from'].addresses[0].username)
for part in msg.walk():
counter = 1
# 如果maintype是multipart,说明是容器(用于包含正文、附件等)
if part.get_content_maintype() == 'multipart':
continue
elif part.get_content_maintype() == 'text':
print(part.get_content())
# 处理附件
else:
filename = part.get_filename()
if not filename:
# 根据附件的contnet_type来推测它的后缀名
ext = mimetypes.guess_extension(part.get_content_type())
if not ext:
ext = '.bin'
# 程序为附件来生成文件名
filename = 'part-%03d%s' % (counter, ext)
counter += 1
# 将附件写入本地文件
with open(os.path.join('.', filename), 'wb') as fp:
fp.write(part.get_payload(decode=True))
# 退出服务器,相当于发送POP 3的quit命令
conn.quit()