Python 自动化通信:发送邮件与短信

前两篇我们已经用 Daoman Python AI 实现了 Excel、Word、PDF 的一键自动生成——但做出来没人收可白搭!今天就打通业务最后一公里:聊聊 Python 怎么用 SMTP 协议无缝对接邮件,以及靠第三方 HTTP API 搞定合规短信通知,直接把「文档产出→消息触达」的闭环焊死~


1. 发送电子邮件:用 SMTP 直接「敲门」邮件服务器

发邮件不一定非要开浏览器登录,本质是我们的程序模拟邮件客户端,通过 SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)直接和网易/QQ/企业邮箱的服务器对话。Python 内置的 smtplib 刚好封装了底层的握手、加密、发送逻辑。

1.1 前置安全准备:拿到「专属钥匙」

第三方程序直接用邮箱登录密码风险太高,现在主流邮箱(126/163/QQ/Gmail)都要求用授权码替代:

  1. 登录邮箱网页版
  2. 进入「设置→账户/POP3/IMAP/SMTP/Exchange」
  3. 开启 POP3/SMTP 服务
  4. 按提示绑定手机号、发送验证短信,拿到16位左右的专属授权码(记得复制保存,只显示一次!)

💡 小提示:企业邮箱的授权码/配置可能略有不同,但原理一致,查IT文档或找运维就行

1.2 第一封:纯文本邮件快速上手

邮件内容由 email.mime 系列模块「拼」出来,纯文本最简单,用 MIMEText;如果要附件/HTML/图片,就得用「容器」 MIMEMultipart

import smtplib
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def send_plain_email():
    # 1. 创建邮件容器(就算纯文本也建议用容器,方便后续扩展附件)
    msg = MIMEMultipart()
    msg['From'] = 'daoman_ai_demo@126.com'  # 你的发送邮箱
    msg['To'] = 'recipient@example.com'     # 接收邮箱,多个用逗号分隔字符串
    msg['Subject'] = Header('Daoman Python AI 季度报表通知', 'utf-8')  # 防止中文主题乱码

    # 2. 添加纯文本正文
    plain_body = "您好李总,\n\n您申请的三季度自动化销售报表已通过 Daoman Python AI 生成完成,如需添加附件/自定义HTML请告诉我哦~\n\n此致\n敬礼\n道满科技技术部"
    msg.attach(MIMEText(plain_body, 'plain', 'utf-8'))  # 'plain'标记是纯文本

    # 3. 连接SMTP服务器(用SSL加密端口更安全,主流邮箱SSL端口465,非加密25,后者容易被封)
    try:
        # 替换为你的邮箱对应SMTP地址:126是smtp.126.com,QQ是smtp.qq.com
        smtp_server = smtplib.SMTP_SSL('smtp.126.com', 465)
        # 登录:用发送邮箱的**完整地址**和刚才拿到的**授权码**
        smtp_server.login('daoman_ai_demo@126.com', '你的专属授权码')
        # 发送:as_string()把整个邮件容器转成标准邮件格式
        smtp_server.sendmail(
            from_addr='daoman_ai_demo@126.com',
            to_addrs=['recipient@example.com'],  # 这里必须是列表!哪怕只有一个
            msg=msg.as_string()
        )
        print("✅ 纯文本邮件发送成功!")
    except Exception as e:
        print(f"❌ 发送失败:{str(e)}")
    finally:
        # 不管成功失败都要断开连接
        smtp_server.quit()

# 调用函数试试
send_plain_email()

1.3 升级版:带附件+HTML排版的邮件

纯文本太干了,我们可以加点HTML美化(比如加表格、加粗、公司logo占位),再把刚才AI生成的Excel/PDF/Word塞进去~

附件的核心逻辑

不管是.docx/.pdf还是.jpg/.xlsx,都是二进制文件,SMTP只能传文本,所以得用 Base64 把二进制转成字符。另外要特别注意中文文件名乱码问题,用 urllib.parse.quote 处理一下。

import smtplib
from urllib.parse import quote
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication  # 比MIMEText(base64)更规范!

def send_fancy_email():
    msg = MIMEMultipart()
    msg['From'] = 'daoman_ai_demo@126.com'
    msg['To'] = 'recipient@example.com'
    msg['Subject'] = Header('【重要】三季度销售分析报告', 'utf-8')

    # 2. 添加带HTML的正文(也可以加plain作为备选,防止收件人客户端不支持HTML)
    html_body = """
    <html>
      <body>
        <p>亲爱的李总,</p>
        <p>您申请的<strong>202X年三季度华东区销售分析报告</strong>已由Daoman Python AI自动生成,包含以下文件:</p>
        <ul>
          <li>原始销售数据汇总.xlsx</li>
          <li>可视化图表分析报告.pdf</li>
          <li>精简版Word汇报.docx</li>
        </ul>
        <p>请查收附件,如有问题随时联系技术部!</p>
        <br>
        <p style="color: #888; font-size: 12px;">道满科技技术部 · 自动生成邮件</p>
      </body>
    </html>
    """
    # 先加plain再add HTML?或者反过来?建议plain在后(某些客户端优先显示最后加的),或者用'alternative'子容器
    msg.attach(MIMEText(html_body, 'html', 'utf-8'))

    # 3. 批量添加附件(封装成小列表循环更优雅)
    attachment_list = [
        {'path': 'resources/202XQ3_销售数据.xlsx', 'name': '202X年三季度华东区销售数据.xlsx'},
        {'path': 'resources/202XQ3_可视化分析.pdf', 'name': '202X年三季度华东区销售分析.pdf'}
    ]

    for att in attachment_list:
        with open(att['path'], 'rb') as f:
            # MIMEApplication会自动处理Base64编码,Content-Type默认application/octet-stream(通用二进制)
            part = MIMEApplication(f.read())
            # 防止中文文件名乱码的标准写法(兼容Outlook/网易/QQ邮箱)
            part.add_header(
                'Content-Disposition',
                'attachment',
                filename=('utf-8', '', att['name'])
            )
            msg.attach(part)

    # 4. 连接发送(和纯文本逻辑一样)
    try:
        smtp_server = smtplib.SMTP_SSL('smtp.126.com', 465)
        smtp_server.login('daoman_ai_demo@126.com', '你的专属授权码')
        smtp_server.sendmail(
            'daoman_ai_demo@126.com',
            ['recipient@example.com'],
            msg.as_string()
        )
        print("✅ 带附件+HTML的邮件发送成功!")
    except Exception as e:
        print(f"❌ 发送失败:{str(e)}")
    finally:
        smtp_server.quit()

send_fancy_email()

2. 发送短信:找第三方网关当「中间商」

和邮件有统一的SMTP协议不一样,短信没有通用的编程协议,总不能自己建个基站吧?所以我们需要找合规的第三方短信服务商(比如阿里云、腾讯云、螺丝帽)当「中间商」,用它们提供的 HTTP API 发送。

2.1 短信发送的标准合规流程

国家对短信管控很严,不带签名/未经许可发营销短信是违法的!所以必须严格走这几步:

  1. 注册服务商账号:选一个稳定的(推荐阿里云/腾讯云,大厂合规性好、有技术支持)
  2. 实名认证企业/个人
  3. 申请短信签名:比如【道满科技】,放在短信开头或结尾(服务商有审核标准,不能太随意)
  4. 申请短信模板:比如您的验证码是${code},请于5分钟内输入。请勿泄露给他人!,变量用服务商指定的占位符
  5. 获取API Key/Secret Key/Access Key:相当于调用接口的「钥匙」

2.2 实战代码(以阿里云短信服务为例)

大厂的API更规范,支持一键生成SDK,但这里用通用的 requests 库演示,方便大家迁移到其他服务商~

import requests
import json
from hashlib import sha256
import hmac
import base64
from datetime import datetime
import urllib.parse

def send_aliyun_sms(phone, code):
    """
    发送阿里云短信验证码
    需要提前在阿里云开通短信服务、申请签名、申请模板
    """
    # 1. 配置你的阿里云信息(这些千万不要硬编码!!建议用环境变量)
    access_key_id = "你的AccessKey ID"
    access_key_secret = "你的AccessKey Secret"
    sign_name = "道满科技"          # 必须是已审核通过的签名
    template_code = "SMS_123456789" # 必须是已审核通过的验证码模板
    template_param = json.dumps({"code": code})  # 模板变量转JSON字符串

    # 2. 阿里云API的签名算法(这步可以直接复制,阿里云文档有标准实现)
    def get_signature(params):
        sorted_params = sorted(params.items())
        canonicalized_query_string = urllib.parse.urlencode(sorted_params)
        string_to_sign = f"GET&%2F&{urllib.parse.quote_plus(canonicalized_query_string)}"
        key = f"{access_key_secret}&".encode('utf-8')
        message = string_to_sign.encode('utf-8')
        h = hmac.new(key, message, sha256)
        signature = base64.b64encode(h.digest()).decode('utf-8')
        return signature

    # 3. 构建请求参数
    common_params = {
        "SignatureMethod": "HMAC-SHA1",
        "SignatureNonce": str(int(datetime.now().timestamp() * 1000000)),
        "SignatureVersion": "1.0",
        "AccessKeyId": access_key_id,
        "Timestamp": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
        "Format": "JSON",
        "Action": "SendSms",
        "Version": "2017-05-25",
        "RegionId": "cn-hangzhou",
        "PhoneNumbers": phone,
        "SignName": sign_name,
        "TemplateCode": template_code,
        "TemplateParam": template_param
    }

    # 4. 生成签名并拼接到URL
    signature = get_signature(common_params)
    common_params['Signature'] = signature
    request_url = f"https://dysmsapi.aliyuncs.com/?{urllib.parse.urlencode(common_params)}"

    # 5. 发起GET请求
    try:
        resp = requests.get(request_url, timeout=10)
        result = resp.json()
        if result.get("Code") == "OK":
            print(f"✅ 短信下发成功!手机号:{phone}")
            return True
        else:
            print(f"❌ 短信下发失败:{result.get('Message')}")
            return False
    except Exception as e:
        print(f"❌ 网络请求失败:{str(e)}")
        return False

# 调用函数试试(替换成真实手机号)
send_aliyun_sms("13800000000", "666888")

3. 避坑指南与安全建议

  1. 🔐 绝对安全第一: 千万不要把授权码、API Key/Secret 直接写在代码里(尤其是传到GitHub/GitLab的公开仓库!),建议用 python-dotenv 库读取环境变量,或者用云服务商的密钥管理服务(KMS)。

  2. 📧 邮件防垃圾信小技巧

    • 不要批量发送一模一样的邮件,加一点个性化内容(比如开头的名字)
    • 尽量用企业邮箱发送(免费邮箱容易被标记)
    • 附件大小控制在10MB以内(大文件可以用云盘链接代替)
  3. 📱 短信合规注意

    • 个人只能发验证码类、通知类短信,不能发营销短信
    • 营销短信必须加「退订回T」
    • 一定要配置IP白名单调用频率限制,防止接口被盗刷

好了,今天的自动化通信教程就到这里!结合前两篇的自动生成文档,你已经可以用 Daoman Python AI 搭建一个完整的「周报/月报自动化系统」啦~