Python 3.12 调用SMTP发送邮件实战:QQ/163邮箱授权码配置与3类常见错误排查
Python 3.12 邮件自动化实战QQ/163邮箱授权码配置与高效发送技巧1. 为什么需要授权码机制现代邮箱服务商普遍采用授权码替代直接密码验证这是基于安全考量的重要升级。以QQ邮箱和163邮箱为例当你在第三方应用如Python脚本中使用SMTP服务时必须使用16位授权码而非邮箱登录密码。授权码机制的核心优势在于风险隔离每个应用可生成独立授权码避免主密码泄露权限可控可随时撤销特定应用的访问权限操作追溯异常登录行为更容易被识别# 授权码与密码的对比 授权码 ABCDEFG1234567890 # 示例格式实际为随机生成 密码 MyPassword123! # 实际邮箱登录密码重要提示授权码仅在生成时显示一次请立即妥善保存。若遗忘需重新生成旧授权码将自动失效。2. 获取邮箱授权码全流程2.1 QQ邮箱授权码配置登录网页版QQ邮箱mail.qq.com点击顶部设置 → 账户找到POP3/IMAP/SMTP服务区块点击生成授权码完成短信验证复制生成的16位授权码建议保存到密码管理器关键参数表服务类型服务器地址端口加密方式SMTPsmtp.qq.com465SSLIMAPimap.qq.com993SSL2.2 163邮箱授权码配置登录网页版163邮箱mail.163.com点击顶部设置 → POP3/SMTP/IMAP开启IMAP/SMTP服务通过手机验证获取授权码记录16位授权码区分大小写# 两种邮箱的服务器配置对比 config { QQ: { smtp_server: smtp.qq.com, smtp_port: 465, imap_server: imap.qq.com }, 163: { smtp_server: smtp.163.com, smtp_port: 465, imap_server: imap.163.com } }3. Python邮件发送实战代码3.1 基础文本邮件发送import smtplib from email.mime.text import MIMEText def send_text_email(sender, auth_code, recipient, subject, content): msg MIMEText(content, plain, utf-8) msg[From] sender msg[To] recipient msg[Subject] subject try: with smtplib.SMTP_SSL(smtp.163.com, 465) as server: server.login(sender, auth_code) server.sendmail(sender, [recipient], msg.as_string()) return True except Exception as e: print(f发送失败: {str(e)}) return False # 使用示例 send_text_email( senderyour_email163.com, auth_codeyour_auth_code, recipienttargetexample.com, subjectPython邮件测试, content这是一封来自Python的测试邮件 )3.2 发送HTML格式邮件from email.mime.multipart import MIMEMultipart def send_html_email(sender, auth_code, recipient, subject, html_content): msg MIMEMultipart(alternative) msg[From] sender msg[To] recipient msg[Subject] subject html_part MIMEText(html_content, html, utf-8) msg.attach(html_part) try: with smtplib.SMTP_SSL(smtp.qq.com, 465) as server: server.login(sender, auth_code) server.sendmail(sender, [recipient], msg.as_string()) return True except Exception as e: print(fHTML邮件发送失败: {str(e)}) return False3.3 带附件的邮件发送from email.mime.base import MIMEBase from email import encoders def send_email_with_attachment(sender, auth_code, recipient, subject, content, file_path): msg MIMEMultipart() msg[From] sender msg[To] recipient msg[Subject] subject # 添加正文 msg.attach(MIMEText(content, plain, utf-8)) # 添加附件 with open(file_path, rb) as f: part MIMEBase(application, octet-stream) part.set_payload(f.read()) encoders.encode_base64(part) part.add_header( Content-Disposition, fattachment; filename{os.path.basename(file_path)} ) msg.attach(part) try: with smtplib.SMTP_SSL(smtp.163.com, 465) as server: server.login(sender, auth_code) server.sendmail(sender, [recipient], msg.as_string()) return True except Exception as e: print(f附件邮件发送失败: {str(e)}) return False4. 常见错误排查指南4.1 认证失败535 Error症状smtplib.SMTPAuthenticationError: (535, bError: authentication failed)解决方案确认使用的是授权码而非邮箱密码检查授权码是否包含空格或特殊字符验证邮箱服务是否已开启SMTP功能尝试重新生成授权码4.2 连接超时症状socket.timeout: timed out排查步骤检查网络连接是否正常验证SMTP服务器地址和端口是否正确尝试关闭防火墙临时测试对于公司网络可能需要联系IT部门开放465端口4.3 编码错误症状UnicodeEncodeError: ascii codec cant encode characters解决方法# 确保所有字符串都明确指定编码 subject 中文主题.encode(utf-8) content 中文内容.encode(utf-8) msg MIMEText(content.decode(utf-8), plain, utf-8)4.4 发送被拒绝错误示例smtplib.SMTPRecipientsRefused: {recipientexample.com: (550, bUser not found)}处理方案检查收件人地址拼写验证发件邮箱是否有发送限制如每日发信量对于163邮箱新注册账号可能需要先手动发送几封邮件激活SMTP功能5. 高级技巧与最佳实践5.1 邮件模板引擎使用Jinja2创建动态邮件模板from jinja2 import Template template Template( html body p尊敬的 {{ username }}/p p您的订单 {{ order_id }} 已发货预计{{ delivery_date}}送达。/p /body /html ) html_content template.render( username张三, order_id2023123456, delivery_date2023-12-25 )5.2 批量发送优化import time def batch_send_emails(sender, auth_code, recipients, subject, content, delay1): successes [] failures [] with smtplib.SMTP_SSL(smtp.qq.com, 465) as server: server.login(sender, auth_code) for recipient in recipients: try: msg MIMEText(content, plain, utf-8) msg[From] sender msg[To] recipient msg[Subject] subject server.sendmail(sender, [recipient], msg.as_string()) successes.append(recipient) time.sleep(delay) # 避免触发反垃圾机制 except Exception as e: failures.append((recipient, str(e))) return successes, failures5.3 安全性增强建议授权码存储使用环境变量或加密配置文件保存授权码import os from dotenv import load_dotenv load_dotenv() AUTH_CODE os.getenv(EMAIL_AUTH_CODE)连接验证发送前检查SMTP服务可用性def check_smtp_connection(server, port): try: with smtplib.SMTP_SSL(server, port, timeout5) as conn: return True except: return False异常处理实现自动重试机制from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10)) def send_email_with_retry(msg): # 发送逻辑在实际项目中我通常会创建一个EmailClient类来封装所有邮件操作这样既保持了代码整洁又方便统一管理配置和异常处理。对于关键业务邮件建议添加邮件送达状态跟踪功能可以通过在邮件中嵌入追踪像素或使用专业的邮件发送服务API实现。