用手机扫码上面的二维码可以通过发送短信的方式来获取授权码,短信发送成功后,点击“我已发送”就可以获得授权码。授权码需要妥善保管,因为一旦泄露就会被其他人冒用你的身份来发送邮件。接下来,我们就可以编写发送邮件的代码了,如下所示。
先实现最基础的纯文本邮件发送,代码加详细注解,替换占位符即可用:
import smtplibfrom email.header import Headerfrom email.mime.multipart import MIMEMultipartfrom email.mime.text import MIMEText# 创建邮件主体对象email = MIMEMultipart()# 设置发件人、收件人和主题email['From'] = 'xxxxxxxxx@126.com'email['To'] = 'yyyyyy@qq.com;zzzzzz@1000phone.com'email['Subject'] = Header('上半年工作情况汇报', 'utf-8')# 添加邮件正文内容content = """据德国媒体报道,当地时间9日,德国火车司机工会成员进行了投票,定于当地时间10日起进行全国性罢工,货运交通方面的罢工已于当地时间10日19时开始。此后,从11日凌晨2时到13日凌晨2时,德国全国范围内的客运和铁路基础设施将进行48小时的罢工。"""email.attach(MIMEText(content, 'plain', 'utf-8'))# 创建SMTP_SSL对象(连接邮件服务器)smtp_obj = smtplib.SMTP_SSL('smtp.126.com', 465)# 通过用户名和授权码进行登录smtp_obj.login('xxxxxxxxx@126.com', '邮件服务器的授权码')# 发送邮件(发件人、收件人、邮件内容(字符串))smtp_obj.sendmail('xxxxxxxxx@126.com', ['yyyyyy@qq.com', 'zzzzzz@1000phone.com'], email.as_string())
如果要发送带有附件的邮件,只需要将附件的内容处理成 BASE64 编码,那么它就和普通的文本内容几乎没有什么区别。BASE64 是一种基于 64 个可打印字符来表示二进制数据的表示方法,常用于某些需要表示、传输、存储二进制数据的场合,电子邮件就是其中之一。对这种编码方式不理解的同学,推荐阅读《Base64笔记》一文。在之前的内容中,我们也提到过,Python 标准库的base64模块提供了对 BASE64 编解码的支持。
4.实战 2:发送带附件的邮件(支持中文文件名)
工作中常需发送文档 / 表格附件,核心是将附件转 Base64 编码(二进制数据转文本,适配邮件传输),并处理中文附件名乱码问题:
import smtplibfrom email.header import Headerfrom email.mime.multipart import MIMEMultipartfrom email.mime.text import MIMETextfrom urllib.parse import quote# 创建邮件主体对象email = MIMEMultipart()# 设置发件人、收件人和主题email['From'] = 'xxxxxxxxx@126.com'email['To'] = 'zzzzzzzz@1000phone.com'email['Subject'] = Header('请查收离职证明文件', 'utf-8')# 添加邮件正文内容(带HTML标签排版的内容)content = """<p>亲爱的前同事:</p><p>你需要的离职证明在附件中,请查收!</p><br><p>祝,好!</p><hr><p>孙美丽 即日</p>"""email.attach(MIMEText(content, 'html', 'utf-8'))# 读取作为附件的文件withopen(f'resources/王大锤离职证明.docx', 'rb') as file: attachment = MIMEText(file.read(), 'base64', 'utf-8')# 指定内容类型 attachment['content-type'] = 'application/octet-stream'# 将中文文件名处理成百分号编码 filename = quote('王大锤离职证明.docx')# 指定如何处置内容 attachment['content-disposition'] = f'attachment; filename="{filename}"'# 创建SMTP_SSL对象(连接邮件服务器)smtp_obj = smtplib.SMTP_SSL('smtp.126.com', 465)# 通过用户名和授权码进行登录smtp_obj.login('xxxxxxxxx@126.com', '邮件服务器的授权码')# 发送邮件(发件人、收件人、邮件内容(字符串))smtp_obj.sendmail('xxxxxxxxx@126.com','zzzzzzzz@1000phone.com', email.as_string())
5.通用封装:一键发送邮件(支持多收件人 / 多附件)
将上述逻辑封装成函数,后续只需修改配置即可复用,支持多收件人、多附件:
import smtplibfrom email.header import Headerfrom email.mime.multipart import MIMEMultipartfrom email.mime.text import MIMETextfrom urllib.parse import quote# 邮件服务器域名(自行修改)EMAIL_HOST = 'smtp.126.com'# 邮件服务端口(通常是465)EMAIL_PORT = 465# 登录邮件服务器的账号(自行修改)EMAIL_USER = 'xxxxxxxxx@126.com'# 开通SMTP服务的授权码(自行修改)EMAIL_AUTH = '邮件服务器的授权码'defsend_email(*, from_user, to_users, subject='', content='', filenames=[]):"""发送邮件 :param from_user: 发件人 :param to_users: 收件人,多个收件人用英文分号进行分隔 :param subject: 邮件的主题 :param content: 邮件正文内容 :param filenames: 附件要发送的文件路径 """ email = MIMEMultipart() email['From'] = from_user email['To'] = to_users email['Subject'] = subject message = MIMEText(content, 'plain', 'utf-8') email.attach(message)for filename in filenames:withopen(filename, 'rb') as file: pos = filename.rfind('/') display_filename = filename[pos + 1:] if pos >= 0else filename display_filename = quote(display_filename) attachment = MIMEText(file.read(), 'base64', 'utf-8') attachment['content-type'] = 'application/octet-stream' attachment['content-disposition'] = f'attachment; filename="{display_filename}"' email.attach(attachment) smtp = smtplib.SMTP_SSL(EMAIL_HOST, EMAIL_PORT) smtp.login(EMAIL_USER, EMAIL_AUTH) smtp.sendmail(from_user, to_users.split(';'), email.as_string())
二.发送短信:对接第三方平台(螺丝帽为例)
短信发送无法直接实现,需对接第三方短信平台(提供短信网关和通道),下面以 “螺丝帽” 平台为例,实现验证码 / 通知短信发送。
1. 准备工作
2. 实战:发送验证码短信(封装函数)
用requests库调用平台 HTTP 接口,实现短信发送,补充异常处理和验证码生成逻辑:
import randomimport requestsdefsend_message_by_luosimao(tel, message):"""发送短信(调用螺丝帽短信网关)""" resp = requests.post( url='http://sms-api.luosimao.com/v1/send.json', auth=('api', 'key-注册成功后平台分配的KEY'), data={'mobile': tel,'message': message }, timeout=10, verify=False )return resp.json()defgen_mobile_code(length=6):"""生成指定长度的手机验证码"""return''.join(random.choices('0123456789', k=length))defmain(): code = gen_mobile_code() message = f'您的短信验证码是{code},打死也不能告诉别人哟!【Python小课】'print(send_message_by_luosimao('13500112233', message))if __name__ == '__main__': main()
上面请求螺丝帽的短信网关http://sms-api.luosimao.com/v1/send.json会返回JSON格式的数据,如果返回{'error': 0, 'msg': 'OK'}就说明短信已经发送成功了,如果error的值不是0,可以通过查看官方的开发文档了解到底哪个环节出了问题。螺丝帽平台常见的错误类型如下图所示。