Python-100-Days 实战:用 Python 自动发送邮件与短信(smtplib + 螺丝帽短信网关)

发布时间:2026/10/1 16:11:52
Python-100-Days 实战:用 Python 自动发送邮件与短信(smtplib + 螺丝帽短信网关) 文档教程【免费下载链接】Python-100-DaysPython - 100天从新手到大师项目地址https://gitcode.com/GitHub_Trending/py/Python-100-Days点击查看免费下载本指南以 Python-100-Days 项目第 29 课为核心系统讲解如何用 Python 标准库smtplib发送纯文本邮件和带附件邮件并通过第三方短信平台螺丝帽的 HTTP 网关发送短信验证码。读完本文你将掌握邮件服务器接入、MIME 邮件封装、授权码登录、短信网关调用、验证码生成与业务集成等完整能力可直接用于报表推送、通知提醒、验证码登录等真实业务场景。场景引入让程序替你完成“发送”这件事在前面课程中我们已经学会用 Python 程序自动生成 Excel、Word、PDF 文档见 Day21-30 的 21.文件读写和异常处理.md、24.Python读写Excel文件-1.md、26.Python操作Word和PowerPoint文件.md。更进一步我们完全可以通过电子邮件将生成好的文档发送给指定收件人再用短信告知对方“邮件已发出”。这两类任务都可以用 Python 程序轻松自动化。在即时通信软件如此发达的今天电子邮件依然是互联网上使用最广泛的应用之一公司向应聘者发送录用通知、网站向用户发送账号激活链接、银行向客户推送理财产品几乎都是程序自动完成的。短信同样如此——注册码、验证码、营销信息基本都通过短信通道触达用户。本章即围绕这两个场景展开。发送电子邮件从 SMTP 协议到 smtplib 库SMTP 协议与 smtplib 的关系我们平时用 HTTP超文本传输协议访问网站资源HTTP 是建立在 TCP传输控制协议之上的应用级协议TCP 为众多应用级协议提供了可靠的数据传输服务。而发送电子邮件使用的是 SMTPSimple Mail Transfer Protocol简单邮件传输协议它同样是建立在 TCP 之上的应用级协议规定了邮件的发送者如何与邮件服务器通信的细节。Python 通过标准库smtplib将这些底层操作封装为SMTP_SSL对象我们只需要调用该对象的两个方法即可完成发信login(user, password)用邮箱账号和授权码登录邮件服务器sendmail(from_addr, to_addrs, msg)发送邮件参数分别为发件人地址、收件人地址可传列表表示多个收件人和邮件内容字符串。同时配合标准库email模块email.mime.multipart.MIMEMultipart、email.mime.text.MIMEText、email.header.Header来构造符合 MIME 规范的邮件主体再通过email.as_string()将邮件对象序列化为字符串交给sendmail发送。前提开启邮件服务商的 SMTP 服务并获取授权码发送邮件首先需要接入一台邮件服务器。自己架设邮件服务器对新手并不友好更常见的做法是使用第三方邮箱服务。以网易 126 邮箱为例注册账号并登录后在“设置 → POP3/SMTP/IMAP”中开启 SMTP 服务即可获得一台可用的邮件服务器。开启服务时页面会引导用户用手机发送一条指定内容的短信进行身份验证验证通过后即可获得授权码。授权码需要妥善保管一旦泄露其他人就能冒用你的身份发送邮件。在代码中授权码承担的是邮箱“密码”的角色。发送纯文本邮件最小可运行示例下面这段代码发送一封最简单的邮件无附件、无图片、无其他超文本内容。import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText # 创建邮件主体对象 email MIMEMultipart() # 设置发件人、收件人和主题 email[From] xxxxxxxxx126.com email[To] yyyyyyqq.com;zzzzzz1000phone.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(xxxxxxxxx126.com, 邮件服务器的授权码) # 发送邮件发件人、收件人、邮件内容字符串 smtp_obj.sendmail( xxxxxxxxx126.com, [yyyyyyqq.com, zzzzzz1000phone.com], email.as_string() )关键点解读MIMEMultipart()创建的是一个多部分multipart邮件容器正文、附件等都以独立 MIME 部分的形式attach进去email[From]、email[To]、email[Subject]设置邮件头部中文主题需要用Header(..., utf-8)包装避免乱码MIMEText(content, plain, utf-8)表示添加纯文本正文第三个参数是字符编码SMTP_SSL(smtp.126.com, 465)使用 SSL 加密连接邮件服务器126 邮箱的 SMTP 服务器为smtp.126.comSSL 端口为 465sendmail的第二个参数传列表即可实现一封邮件同时发给多个收件人。发送带附件的邮件BASE64 编码与中文文件名处理如果要发送带附件的邮件只需把附件内容处理成BASE64 编码它与普通文本内容在邮件传输层面几乎没有区别。BASE64 是一种基于 64 个可打印字符A-Z、a-z、0-9、、/来表示二进制数据的编码方法每 3 个字节的二进制数据编码为 4 个可打印字符常用于需要表示、传输、存储二进制数据的场合电子邮件就是其中之一。Python 标准库中的base64模块提供了完整的编解码支持MIMEText也内置了对base64子类型的处理。下面的代码演示了如何发送一封带附件、且正文使用 HTML 排版的邮件import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from urllib.parse import quote # 创建邮件主体对象 email MIMEMultipart() # 设置发件人、收件人和主题 email[From] xxxxxxxxx126.com email[To] zzzzzzzz1000phone.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)) # 读取作为附件的文件 with open(fresources/王大锤离职证明.docx, rb) as file: attachment MIMEText(file.read(), base64, utf-8) # 指定内容类型 attachment[content-type] application/octet-stream # 将中文文件名处理成百分号编码 filename quote(王大锤离职证明.docx) # 指定如何处置内容 attachment[content-disposition] fattachment; filename{filename} # 创建SMTP_SSL对象连接邮件服务器 smtp_obj smtplib.SMTP_SSL(smtp.126.com, 465) # 通过用户名和授权码进行登录 smtp_obj.login(xxxxxxxxx126.com, 邮件服务器的授权码) # 发送邮件发件人、收件人、邮件内容字符串 smtp_obj.sendmail( xxxxxxxxx126.com, zzzzzzzz1000phone.com, email.as_string() )这段代码的三个关键技巧附件编码以二进制模式rb读取文件内容用MIMEText(file.read(), base64, utf-8)将附件包装为 MIME 子类型base64的文本部分内容类型声明通过attachment[content-type] application/octet-stream告知客户端这是一个通用二进制文件中文文件名转码urllib.parse.quote()将中文文件名转换为百分号编码URL 编码再通过attachment[content-disposition] attachment; filename...指定收件人下载时保存的文件名避免中文附件名乱码。同时注意正文类型从plain换成了html收件人客户端会按 HTML 渲染这段正文p、hr等标签生效。封装成通用发信函数一个参数搞定邮件推送为了在实际项目中复用可将上述逻辑封装为一个通用函数使用时只需要调整邮件服务器域名、端口、用户名和授权码四个常量即可。该函数完整代码见 29.Python发送邮件和短信.md核心实现如下import smtplib from email.header import Header from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from urllib.parse import quote # 邮件服务器域名自行修改 EMAIL_HOST smtp.126.com # 邮件服务端口通常是465 EMAIL_PORT 465 # 登录邮件服务器的账号自行修改 EMAIL_USER xxxxxxxxx126.com # 开通SMTP服务的授权码自行修改 EMAIL_AUTH 邮件服务器的授权码 def send_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: with open(filename, rb) as file: pos filename.rfind(/) display_filename filename[pos 1:] if pos 0 else filename display_filename quote(display_filename) attachment MIMEText(file.read(), base64, utf-8) attachment[content-type] application/octet-stream attachment[content-disposition] fattachment; 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())从源码结构可以梳理出这个封装函数的设计要点强制关键字参数形参列表中的*要求所有参数必须以关键字形式传入避免调用时位置参数顺序写错多收件人支持to_users用英文分号;分隔多个收件人函数内部通过to_users.split(;)拆成列表交给sendmail与“设置To头时用分号分隔”保持一致附件批量处理循环遍历filenames每个附件都独立执行 BASE64 编码 content-type/content-disposition设置rfind(/)用于从完整路径中提取纯文件名兼容C:/xxx这类带目录前缀的路径中文附件名每个显示文件名都用quote()转成百分号编码保证收件端不乱码。实战建议filenames[]作为默认参数属于“可变默认参数”在 Python 中多次调用且传入不同附件列表时默认值会被复用可能带来状态残留。更稳妥的写法是用filenamesNone在函数体内再转为空列表。此外调用结束后可补充smtp.quit()显式关闭连接避免资源泄漏。发送短信接入螺丝帽短信网关短信业务与三方平台的接入思路发送短信同样是项目中的高频功能网站注册码、登录验证码、营销信息基本都是通过短信触达用户的。发送短信需要第三方短信平台的支持本课以螺丝帽平台为例演示接入方式。注册账号、购买短信服务的细节不做赘述多数平台提供少量免费测试短信可直接咨询平台客服。仓库中 Day46-60/57.接入三方平台.md 对这类平台的接入流程做了完整归纳步骤如下在短信平台注册账号并登录管理后台进入“短信”版块找到并记录自己的API Key调用短信网关时的认证凭证配置短信签名——短信内容必须携带签名例如“【Python小课】”或免费试用专用的“【铁壳测试】”否则短信无法发送配置IP 白名单——把发起请求的服务器公网 IP 填入白名单本地调试可查询本机公网 IP防止短信接口被他人盗用确认剩余短信条数不足时可在“充值”页面购买短信套餐。调用短信网关发送验证码螺丝帽平台提供 REST 风格的短信网关接口。我们可以通过requests库向短信网关发起一个 HTTP POST 请求把手机号和短信内容作为表单参数提交。代码如下完整实现见 29.Python发送邮件和短信.mdimport random import requests def send_message_by_luosimao(tel, message): 发送短信调用螺丝帽短信网关 resp requests.post( urlhttp://sms-api.luosimao.com/v1/send.json, auth(api, key-注册成功后平台分配的KEY), data{ mobile: tel, message: message }, timeout10, verifyFalse ) return resp.json() def gen_mobile_code(length6): 生成指定长度的手机验证码 return .join(random.choices(0123456789, klength)) def main(): code gen_mobile_code() message f您的短信验证码是{code}打死也不能告诉别人哟【Python小课】 print(send_message_by_luosimao(13500112233, message)) if __name__ __main__: main()这段代码涉及几个容易忽略的细节requests.post的第一个参数是平台提供的短信网关地址http://sms-api.luosimao.com/v1/send.jsonauth(api, key-...)是 HTTP Basic 认证用户名为固定值api密码为“注册成功后平台分配的 KEY”即上面第 2 步查看到的 API Key形如key-xxxxxxxxdata中mobile是接收短信的手机号message是短信内容示例内容由“验证码 提示语 短信签名”拼接而成timeout10设置请求超时时间为 10 秒避免网络异常时程序长时间挂起verifyFalse表示不对服务器的 HTTPS 证书做校验平台接口为 HTTP 时该参数无效在商业环境中建议保留证书校验或改用平台的 HTTPS 地址gen_mobile_code()用random.choices(0123456789, klength)从数字字符集中有放回地抽取length个字符拼接成验证码默认长度 6 位。响应解析与错误码排查请求螺丝帽短信网关会返回 JSON 格式的数据如果返回{error: 0, msg: OK}说明短信发送成功如果error的值不是0则说明发送失败需要对照平台开发文档排查具体原因。仓库 Day46-60/57.接入三方平台.md 中记录了该平台常见错误码的含义例如-20余额不足需要充值短信条数-32缺少短信签名短信内容未附带已配置的签名或签名不在白名单内。说明同一平台在不同时期的接口文档中返回字段名可能略有差异例如本课正文使用error字段而仓库第 57 课中记录的是err字段。实际开发时以平台官方当前返回为准判断逻辑应兼容“成功码为 0”这一约定。签名、模板与防刷短信功能的工程化细节大多数短信平台都会强制要求短信内容携带签名签名需要在平台管理后台提前配置。下图是本课在螺丝帽平台配置的短信签名“【Python小课】”。此外还有几点工程化注意短信模板涉及敏感内容的短信通常需要提前配置模板模板中预留的占位符如新闻标题、链接在发送时动态填充。仓库公开课 第04次公开课-好玩的Python/好玩的Python.md 中有一个完整示例爬取新闻后将“发现一条您可能感兴趣的新闻 - {标题}详情点击 {链接} 查看。【Python小课】”作为短信内容发送给用户其中模板与签名都需先在平台配置IP 白名单平台为了防范短信接口被盗用会要求设置“IP 白名单”只允许白名单内的服务器 IP 发起请求不清楚配置方式可以咨询平台客服频率限制与有效期Day46-60/57.接入三方平台.md 中的 Django 项目展示了更完整的工程实践通过 Redis 记录vote:block-mobile:{tel}键并设置 60 秒过期阻止用户 60 秒内重复发送验证码同时用vote2:valid-mobile:{tel}保存验证码、10 分钟ex600后过期即验证码有效期 10 分钟再配合正则1[3-9]\d{9}对手机号做前置校验非法的手机号直接返回错误提示而不消耗短信条数。TEL_PATTERN re.compile(r1[3-9]\d{9}) def check_tel(tel): 检查手机号 return TEL_PATTERN.fullmatch(tel) is not None def random_code(length6): 生成随机短信验证码 return .join(random.choices(0123456789, klength))短信平台的选择国内的短信平台很多读者可根据费用预算、短信到达率、使用难易程度等指标选择商业项目中建议购买短信平台的套餐服务以保证额度与到达率。需要特别说明的是短信内容的合规性由平台规则约束违规短信无法发送。结合项目邮件与短信的异步化与部署选型在真实项目中发送邮件/短信通常不是同步阻塞在主流程里的仓库中的商业项目实践可以给我们两点启发异步化Day91-100/95.使用Django开发商业项目.md 展示了通过 Celery 将send_email(from_user, to_user, cc_user, subject, content)封装为异步任务用send_email.delay(...)投递到消息队列避免发信耗时阻塞用户请求部署选型Day91-100/98.项目部署上线和性能调优.md 中列出了邮件服务的两种主流选型——自建邮件服务器Postfix、Sendmail与商业邮件服务如 SendCloud这印证了本课总结的观点。总结发送邮件与发送短信本质上都可以通过调用第三方服务来完成邮件使用标准库smtplibemail模块对接任意支持 SMTP 的邮箱服务关键动作是开启 SMTP 服务、获取授权码、构造 MIME 邮件、login后sendmail带附件时用 BASE64 编码并处理中文文件名短信通过requests向短信平台网关发起 HTTP 请求使用平台分配的 API Key 做认证需配置好签名、模板与 IP 白名单并按返回的 JSON 判断是否成功。在商业项目中建议自己架设邮件服务器或购买三方邮件服务来发送邮件如 Day91-100/98.项目部署上线和性能调优.md 提到的 Postfix、Sendmail、SendCloud 等并在业务代码中做好频率限制、验证码有效期、异常重试与异步化处理这才是生产环境比较靠谱的选择。赞分享文档教程【免费下载链接】Python-100-DaysPython - 100天从新手到大师项目地址https://gitcode.com/GitHub_Trending/py/Python-100-Days点击查看免费下载相关推荐使用 AWS SDK for JavaScript (v3) 与 Amazon Pinpoint 发送邮件和短信使用 AWS SDK for JavaScript v3 与 Amazon Pinpoint 发送邮件和短信 本文以 aws doc sdk examples示例工程教程后端Python-100-Days 公开课实战用 Python 玩转图像处理、微信群发与热点新闻推送Python 100 Days 公开课实战用 Python 玩转图像处理、微信群发与热点新闻推送 本篇文章源自 Python 100 Days 项目「第04次文档教程Python邮件发送终极指南smtplib与email模块详解Python邮件发送终极指南smtplib与email模块详解 想要快速掌握Python邮件发送技巧吗Python内置的 smtplib 和 email 模教程文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考