最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Python+smtplib實(shí)現(xiàn)郵件自動(dòng)發(fā)送功能

 更新時(shí)間:2025年09月11日 08:54:39   作者:憶愿  
工作中總有各種郵件需要定期發(fā)送,手動(dòng)操作不僅繁瑣還容易忘記,Python的smtplib庫完美解決這個(gè)痛點(diǎn),幾行代碼就能搞定郵件自動(dòng)發(fā)送,還能加上附件、HTML格式美化、定時(shí)任務(wù)等花樣玩法,所以本文介紹了基于Python+smtplib實(shí)現(xiàn)郵件自動(dòng)發(fā)送功能,需要的朋友可以參考下

引言

工作中總有各種郵件需要定期發(fā)送,手動(dòng)操作不僅繁瑣還容易忘記。Python的smtplib庫完美解決這個(gè)痛點(diǎn),幾行代碼就能搞定郵件自動(dòng)發(fā)送,還能加上附件、HTML格式美化、定時(shí)任務(wù)等花樣玩法。我用這招把每天半小時(shí)的郵件工作縮減到幾分鐘,工作效率直接起飛。

smtplib是啥玩意兒

smtplib是Python自帶的一個(gè)庫,專門用來發(fā)送郵件的。SMTP(Simple Mail Transfer Protocol)就是簡(jiǎn)單郵件傳輸協(xié)議,是互聯(lián)網(wǎng)上用來發(fā)送郵件的標(biāo)準(zhǔn)。這個(gè)庫把復(fù)雜的協(xié)議細(xì)節(jié)都封裝好了,咱們只需要調(diào)用幾個(gè)簡(jiǎn)單的方法就能發(fā)郵件,不用關(guān)心底層的通信細(xì)節(jié)。

    import smtplib
    from email.mime.text import MIMEText
    from email.header import Header
    
    # 基本用法示例
    sender = 'your_email@example.com'
    receivers = ['receiver@example.com']
    
    # 創(chuàng)建一個(gè)簡(jiǎn)單的郵件
    message = MIMEText('這是測(cè)試郵件,別當(dāng)真~', 'plain', 'utf-8')
    message['From'] = Header("Python自動(dòng)發(fā)送", 'utf-8')
    message['To'] = Header("測(cè)試接收人", 'utf-8')
    message['Subject'] = Header('Python郵件測(cè)試', 'utf-8')
    
    try:
        smtp_obj = smtplib.SMTP('smtp.example.com')  # 郵件服務(wù)器地址
        smtp_obj.login('username', 'password')       # 登錄郵箱
        smtp_obj.sendmail(sender, receivers, message.as_string())
        print("郵件發(fā)送成功")
    except smtplib.SMTPException:
        print("Error: 無法發(fā)送郵件")

看著挺多,實(shí)際就是創(chuàng)建連接、寫點(diǎn)內(nèi)容、登錄、發(fā)送這幾步。

溫馨提示 :這個(gè)代碼敲完不一定能直接跑,因?yàn)楝F(xiàn)在各大郵箱為了安全都開啟了授權(quán)碼登錄,你得到郵箱設(shè)置里去申請(qǐng)一個(gè)授權(quán)碼,用這個(gè)碼來替代密碼登錄。

連接郵箱服務(wù)器,這事有點(diǎn)講究

連郵箱服務(wù)器有兩種方式:普通連接和SSL加密連接?,F(xiàn)在基本都用第二種,安全嘛。

普通連接:

    smtp_obj = smtplib.SMTP('smtp.example.com', 25)  # 25是SMTP協(xié)議的默認(rèn)端口

SSL加密連接:

    smtp_obj = smtplib.SMTP_SSL('smtp.example.com', 465)  # 465是SMTP over SSL的默認(rèn)端口

幾個(gè)常見郵箱的SMTP服務(wù)器地址:

  • Gmail: smtp.gmail.com
  • QQ郵箱: smtp.qq.com
  • 163郵箱: smtp.163.com
  • 阿里企業(yè)郵箱: smtp.qiye.aliyun.com

我在上班的時(shí)候,天天用企業(yè)郵箱自動(dòng)化處理各種通知,賊方便。

登錄郵箱也很簡(jiǎn)單:

    smtp_obj.login('your_email@example.com', '你的授權(quán)碼')

這里有個(gè)大坑,不是用你平時(shí)登錄的密碼,是要到郵箱設(shè)置里申請(qǐng)的專門用于第三方登錄的授權(quán)碼。每個(gè)郵箱申請(qǐng)方式不太一樣:

  • QQ郵箱:設(shè)置 -> 賬戶 -> POP3/SMTP服務(wù)
  • 163郵箱:設(shè)置 -> POP3/SMTP/IMAP -> 開啟服務(wù)
  • 企業(yè)郵箱:一般管理員會(huì)分配

不同郵箱叫法可能不同,像什么"安全碼"、“應(yīng)用專用密碼”、“授權(quán)碼”,反正不是你平時(shí)登錄的那個(gè)密碼就對(duì)了。

也有人問為啥現(xiàn)在郵箱這么麻煩,整這種授權(quán)碼。別抱怨,這是為了安全。想想看,你平時(shí)登錄密碼要是泄露了,一切都完蛋。但授權(quán)碼只能用于郵件收發(fā),黑客就算拿到了也不能改你密碼、不能刪你郵件,安全多了。

發(fā)送純文本郵件,小菜一碟

最基礎(chǔ)的就是發(fā)純文本郵件,真的超簡(jiǎn)單:

    from email.mime.text import MIMEText
    from email.header import Header
    
    # 創(chuàng)建純文本郵件
    msg = MIMEText('這是郵件內(nèi)容,想寫啥寫啥', 'plain', 'utf-8')
    msg['From'] = Header('Python自動(dòng)發(fā)送', 'utf-8')
    msg['To'] = Header('老板', 'utf-8')
    msg['Subject'] = Header('每日工作匯報(bào)', 'utf-8')
    
    # 發(fā)送郵件
    smtp_obj.sendmail(sender, receivers, msg.as_string())

MIMEText 第一個(gè)參數(shù)是郵件內(nèi)容,第二個(gè)參數(shù)指定內(nèi)容類型(plain表示純文本),第三個(gè)參數(shù)指定編碼。

溫馨提示 :收件人可以是一個(gè)列表,一次發(fā)給多個(gè)人:

    receivers = ['boss@example.com', 'colleague@example.com']

順帶提一句,如果你要給很多人發(fā)郵件,但又不想讓他們看到彼此的郵箱地址(避免泄露信息),可以用密送(BCC):

    msg['Cc'] = Header('同事A,同事B', 'utf-8')  # 抄送
    msg['Bcc'] = Header('同事C,同事D', 'utf-8')  # 密送

密送的人會(huì)收到郵件,但其他收件人不會(huì)知道有人被密送。職場(chǎng)暗斗必備技能,老板想悄悄讓你的同事也看看你的工作匯報(bào),哈哈。

發(fā)HTML格式郵件,逼 格瞬間提升

純文本太單調(diào)了,HTML格式可以做出花哨的樣式,還能放圖片、表格啥的:

    html_content = """
    <html>
    <body>
        <h1 style="color:red">項(xiàng)目進(jìn)度報(bào)告</h1>
        <p>各位好:</p>
        <p>本周項(xiàng)目進(jìn)展如下:</p>
        <ul>
            <li>需求分析:<span style="color:green">已完成</span></li>
            <li>系統(tǒng)設(shè)計(jì):<span style="color:green">已完成</span></li>
            <li>編碼:<span style="color:orange">進(jìn)行中 (75%)</span></li>
            <li>測(cè)試:<span style="color:red">未開始</span></li>
        </ul>
        <p>詳情請(qǐng)見<a  rel="external nofollow" >完整報(bào)告</a></p>
    </body>
    </html>
    """
    
    # 創(chuàng)建HTML郵件
    message = MIMEText(html_content, 'html', 'utf-8')
    message['From'] = Header('項(xiàng)目負(fù)責(zé)人', 'utf-8')
    message['To'] = Header('項(xiàng)目組', 'utf-8')
    message['Subject'] = Header('每周項(xiàng)目進(jìn)度', 'utf-8')

就是把 MIMEText 的第二個(gè)參數(shù)從 ‘plain’ 改成 ‘html’ 就行了,內(nèi)容寫HTML代碼。這個(gè)技能真是救命,以前我每周都要手動(dòng)排版項(xiàng)目周報(bào),用了自動(dòng)發(fā)送后,模板一套,數(shù)據(jù)一填,一鍵發(fā)送,省了大把時(shí)間。

需要注意的是,不是所有郵件客戶端都支持所有HTML特性,尤其是那些新潮的CSS3屬性。我有個(gè)同事用了漸變背景和彈性布局,結(jié)果老板的古董Outlook全亂了。建議保守點(diǎn)用HTML 4和基礎(chǔ)CSS,兼容性最好。

要是想放圖片,直接插入外部圖片鏈接即可:

    <img src="https://example.com/logo.png" alt="公司Logo" />

但這要求收件人能訪問這個(gè)圖片鏈接,而且會(huì)暴露IP。更專業(yè)的做法是用 內(nèi)嵌圖片 ,下面我專門講講。

內(nèi)嵌圖片,讓郵件更專業(yè)

郵件里直接嵌入圖片,不依賴外部鏈接,看起來更專業(yè),也不會(huì)被攔截:

    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.image import MIMEImage
    
    # 創(chuàng)建復(fù)合郵件
    message = MIMEMultipart('related')
    message['From'] = Header('營(yíng)銷部門', 'utf-8')
    message['To'] = Header('客戶', 'utf-8')
    message['Subject'] = Header('本月促銷活動(dòng)', 'utf-8')
    
    # HTML內(nèi)容,引用內(nèi)嵌圖片
    html = """
    <html>
    <body>
        <h1>五月促銷活動(dòng)</h1>
        <p>尊敬的客戶:</p>
        <p>查看我們的最新產(chǎn)品:</p>
        <img src="cid:image1" width="500">
        <p>限時(shí)八折優(yōu)惠,歡迎選購!</p>
    </body>
    </html>
    """
    message.attach(MIMEText(html, 'html', 'utf-8'))
    
    # 添加圖片
    with open('product.jpg', 'rb') as f:
        img = MIMEImage(f.read())
        img.add_header('Content-ID', '<image1>')  # 與HTML中的cid對(duì)應(yīng)
        message.attach(img)

注意看,圖片引用是通過 cid:image1 這種特殊語法實(shí)現(xiàn)的。Content-ID 標(biāo)簽必須用尖括號(hào)包住,而HTML中引用時(shí)不用尖括號(hào),這個(gè)細(xì)節(jié)很容易出錯(cuò)。

添加附件,才是郵件發(fā)送的完全體

工作中發(fā)郵件哪能只發(fā)文字啊,各種文檔、Excel表格、圖片啥的都得帶上。添加附件也不復(fù)雜,就是多寫幾行代碼:

    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.application import MIMEApplication
    import os
    
    # 創(chuàng)建帶附件的郵件
    message = MIMEMultipart()
    message['From'] = Header('數(shù)據(jù)分析小組', 'utf-8')
    message['To'] = Header('產(chǎn)品經(jīng)理', 'utf-8')
    message['Subject'] = Header('用戶數(shù)據(jù)分析報(bào)告', 'utf-8')
    
    # 添加郵件正文
    message.attach(MIMEText('附件是本月用戶增長(zhǎng)數(shù)據(jù),請(qǐng)查收。', 'plain', 'utf-8'))
    
    # 添加Excel附件
    excel_path = 'monthly_report.xlsx'
    with open(excel_path, 'rb') as f:
        excel_attachment = MIMEApplication(f.read())
        excel_attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(excel_path))
        message.attach(excel_attachment)
    
    # 添加PDF附件
    pdf_path = 'analysis_report.pdf'
    with open(pdf_path, 'rb') as f:
        pdf_attachment = MIMEApplication(f.read())
        pdf_attachment.add_header('Content-Disposition', 'attachment', filename=os.path.basename(pdf_path))
        message.attach(pdf_attachment)

關(guān)鍵區(qū)別在于用了 MIMEMultipart 類,它允許組合多個(gè)部分成一個(gè)完整郵件。PDF、Excel、Word、圖片都能添加,我的周報(bào)每次自動(dòng)帶上三個(gè)Excel和一份PPT,完全不用手動(dòng)操作。

溫馨提示 :如果附件名含中文,可能會(huì)亂碼,需要特殊處理:

    from email.header import Header
    
    filename = 'sales_report_中文名.xlsx'
    excel_attachment.add_header('Content-Disposition', 'attachment', 
                               filename=('utf-8', '', filename))

附件大小也要注意,大多數(shù)郵箱對(duì)附件有大小限制:

  • Gmail: 25MB
  • QQ郵箱: 50MB
  • 企業(yè)郵箱: 取決于管理員設(shè)置,通常是10MB-50MB

超出限制郵件發(fā)不出去,要么壓縮附件,要么用云盤分享鏈接。

批量發(fā)送個(gè)性化郵件,成為郵件屆的"網(wǎng)紅"

有時(shí)候需要給不同人發(fā)不同內(nèi)容的郵件,比如給客戶發(fā)通知,每個(gè)客戶的稱呼、訂單信息都不同。這時(shí)就需要批量發(fā)送個(gè)性化郵件:

    import pandas as pd
    
    # 假設(shè)有個(gè)客戶信息表格
    df = pd.read_excel('customers.xlsx')
    
    for index, row in df.iterrows():
        # 創(chuàng)建郵件
        msg = MIMEText(f"""尊敬的{row['name']}:
        
    感謝您購買我們的產(chǎn)品。您的訂單號(hào) {row['order_id']} 已發(fā)貨,
    預(yù)計(jì){row['estimated_delivery']}送達(dá)。
    如有問題,請(qǐng)聯(lián)系我們的客服電話:400-123-4567。
    
    祝好,
    銷售團(tuán)隊(duì)
        """, 'plain', 'utf-8')
        
        msg['From'] = Header('客戶服務(wù)部', 'utf-8')
        msg['To'] = Header(row['name'], 'utf-8')
        msg['Subject'] = Header('您的訂單已發(fā)貨', 'utf-8')
        
        # 發(fā)送郵件
        smtp_obj.sendmail('service@example.com', row['email'], msg.as_string())
        print(f"已向 {row['name']} 發(fā)送郵件")
        
        # 適當(dāng)延時(shí),避免郵件服務(wù)器認(rèn)為是垃圾郵件
        import time
        time.sleep(1)

這段代碼會(huì)讀取Excel表格中的客戶信息,然后給每個(gè)客戶發(fā)送包含其個(gè)人信息的郵件。我以前每天手動(dòng)發(fā)的周報(bào)總結(jié),現(xiàn)在全自動(dòng)化了,而且還能根據(jù)不同部門定制不同內(nèi)容,同事們都夸我效率高。

溫馨提示 :批量發(fā)送時(shí)要控制發(fā)送頻率,太快會(huì)被郵件服務(wù)器當(dāng)成垃圾郵件行為,可能導(dǎo)致賬號(hào)被封。最好每封郵件間隔1-5秒。

有個(gè)前同事因?yàn)檫@事吃了大虧,他一下發(fā)了5000封郵件,結(jié)果公司郵箱被服務(wù)商封了,整個(gè)公司幾百人兩天收不到外部郵件,差點(diǎn)被老板炒魷魚。服務(wù)器有發(fā)信額度,通常是每小時(shí)幾百封,具體看你的郵箱服務(wù)協(xié)議。

定時(shí)自動(dòng)發(fā)送,解放你的雙手

手動(dòng)運(yùn)行腳本太low了,設(shè)置定時(shí)任務(wù)才是真正的自動(dòng)化。Python有個(gè)schedule庫,專門用來設(shè)置定時(shí)任務(wù):

    import schedule
    import time
    
    def send_daily_report():
        # 這里放發(fā)送郵件的代碼
        print("發(fā)送每日?qǐng)?bào)告")
    
    def send_weekly_report():
        # 這里放發(fā)送周報(bào)的代碼
        print("發(fā)送周報(bào)")
    
    # 每天上午9點(diǎn)發(fā)送日?qǐng)?bào)
    schedule.every().day.at("09:00").do(send_daily_report)
    
    # 每周一下午5點(diǎn)發(fā)送周報(bào)
    schedule.every().monday.at("17:00").do(send_weekly_report)
    
    # 每月1號(hào)發(fā)送月報(bào)
    schedule.every().month_start.at("10:00").do(lambda: print("發(fā)送月報(bào)"))
    
    # 每隔2小時(shí)發(fā)送一次監(jiān)控報(bào)告
    schedule.every(2).hours.do(lambda: print("發(fā)送監(jiān)控報(bào)告"))
    
    while True:
        schedule.run_pending()
        time.sleep(60)  # 每分鐘檢查一次是否有任務(wù)需要執(zhí)行

不過這種方法需要保持程序一直運(yùn)行,更專業(yè)的做法是用操作系統(tǒng)的定時(shí)任務(wù):

  • Windows: 任務(wù)計(jì)劃程序
  • Linux/Mac: cron jobs

Linux上設(shè)置定時(shí)任務(wù)超簡(jiǎn)單,編輯crontab文件:

    crontab -e

加入以下內(nèi)容設(shè)置定時(shí)任務(wù):

    # 每天早上9點(diǎn)執(zhí)行
    0 9 * * * python /path/to/send_daily_email.py
    
    # 每周一下午5點(diǎn)執(zhí)行
    0 17 * * 1 python /path/to/send_weekly_email.py

我搞過一個(gè)項(xiàng)目監(jiān)控系統(tǒng),服務(wù)器出問題就自動(dòng)發(fā)郵件通知我,大半夜收到郵件爬起來處理問題,救過好幾次火。

Linux的cron表達(dá)式有點(diǎn)難記,尤其是星期幾那部分(0和7都表示周日,1-6表示周一到周六)。一個(gè)小技巧是用在線cron生成工具,比如crontab.guru,可視化編輯cron表達(dá)式,直觀很多。

使用環(huán)境變量保存敏感信息,別把密碼寫在代碼里

直接把郵箱密碼寫在代碼里太危險(xiǎn)了,萬一代碼上傳到GitHub,密碼就泄露了。更安全的做法是使用環(huán)境變量:

    import os
    from dotenv import load_dotenv
    
    # 加載環(huán)境變量
    load_dotenv()
    
    # 從環(huán)境變量獲取敏感信息
    email_user = os.getenv('EMAIL_USER')
    email_password = os.getenv('EMAIL_PASSWORD')
    smtp_server = os.getenv('SMTP_SERVER')
    
    # 使用這些變量連接郵箱
    smtp_obj = smtplib.SMTP_SSL(smtp_server, 465)
    smtp_obj.login(email_user, email_password)

創(chuàng)建一個(gè) .env 文件存儲(chǔ)敏感信息:

    EMAIL_USER=your_email@example.com
    EMAIL_PASSWORD=your_password
    SMTP_SERVER=smtp.example.com

記得把 .env 文件添加到 .gitignore 中,這樣就不會(huì)被上傳到代碼倉庫了。

如果是生產(chǎn)環(huán)境,建議用專門的密鑰管理服務(wù),比如阿里云KMS、AWS Secrets Manager等。在工作時(shí),我們都用RAM角色授權(quán)+KMS管理密鑰,安全性高很多。

處理郵件發(fā)送失敗,加上錯(cuò)誤重試機(jī)制

網(wǎng)絡(luò)不穩(wěn)定、郵件服務(wù)器偶爾抽風(fēng)是常態(tài),必須處理異常并重試:

    def send_email_with_retry(sender, receivers, message, max_retries=3):
        retry_count = 0
        while retry_count < max_retries:
            try:
                smtp_obj = smtplib.SMTP_SSL('smtp.example.com', 465)
                smtp_obj.login('username', 'password')
                smtp_obj.sendmail(sender, receivers, message.as_string())
                smtp_obj.quit()
                print("郵件發(fā)送成功")
                return True  # 發(fā)送成功
            except Exception as e:
                retry_count += 1
                print(f"發(fā)送失敗 (嘗試 {retry_count}/{max_retries}): {str(e)}")
                time.sleep(5)  # 等待5秒后重試
        
        print("發(fā)送郵件失敗,已達(dá)到最大重試次數(shù)")
        return False  # 發(fā)送失敗

這個(gè)函數(shù)會(huì)嘗試發(fā)送郵件,如果失敗會(huì)最多重試3次,每次間隔5秒。實(shí)際生產(chǎn)環(huán)境中,我設(shè)置了重試+告警機(jī)制,發(fā)不出去會(huì)自動(dòng)打電話給我。

各種異常情況都需要考慮:

  • smtplib.SMTPAuthenticationError:賬號(hào)密碼錯(cuò)誤
  • smtplib.SMTPRecipientsRefused:收件人被拒絕
  • smtplib.SMTPSenderRefused:發(fā)件人被拒絕
  • smtplib.SMTPDataError:郵件數(shù)據(jù)錯(cuò)誤
  • smtplib.SMTPServerDisconnected:與服務(wù)器連接中斷
  • socket.gaierror:DNS解析錯(cuò)誤
  • ConnectionRefusedError:連接被拒絕

不同錯(cuò)誤要有不同處理策略。比如認(rèn)證錯(cuò)誤多半是賬號(hào)密碼問題,重試也沒用;但連接中斷可能是網(wǎng)絡(luò)波動(dòng),重試就有可能成功。

記錄郵件發(fā)送日志,做個(gè)有據(jù)可查的人

日志是查問題的關(guān)鍵,要養(yǎng)成記錄日志的好習(xí)慣:

    import logging
    
    # 配置日志
    logging.basicConfig(
        filename='email_sender.log',
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s'
    )
    
    try:
        # 發(fā)送郵件的代碼
        smtp_obj.sendmail(sender, receivers, message.as_string())
        logging.info(f"郵件已成功發(fā)送給 {receivers}")
    except Exception as e:
        logging.error(f"發(fā)送郵件失敗: {str(e)}")

這樣每次發(fā)送的結(jié)果都會(huì)記錄到日志文件中,以后查問題超方便。

溫馨提示 :生產(chǎn)環(huán)境中建議使用更完善的日志方案,比如按日期切割日志文件、設(shè)置日志輪轉(zhuǎn)等。

    import logging
    from logging.handlers import RotatingFileHandler
    
    # 配置按大小輪轉(zhuǎn)的日志
    handler = RotatingFileHandler(
        'email_sender.log', 
        maxBytes=10485760,  # 10MB
        backupCount=5
    )
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    logger.addHandler(handler)

這樣日志文件達(dá)到10MB時(shí)會(huì)自動(dòng)創(chuàng)建新文件,最多保留5個(gè)備份文件,不會(huì)吃光磁盤空間。

完整實(shí)戰(zhàn)例子:每日銷售數(shù)據(jù)自動(dòng)郵件系統(tǒng)

最后來個(gè)完整實(shí)例,整合前面說的所有技巧:

    import smtplib
    import os
    import pandas as pd
    import schedule
    import time
    import logging
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.application import MIMEApplication
    from email.header import Header
    from dotenv import load_dotenv
    from datetime import datetime
    
    # 配置日志
    logging.basicConfig(
        filename='sales_report_sender.log',
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s'
    )
    
    # 加載環(huán)境變量
    load_dotenv()
    EMAIL_USER = os.getenv('EMAIL_USER')
    EMAIL_PASSWORD = os.getenv('EMAIL_PASSWORD')
    SMTP_SERVER = os.getenv('SMTP_SERVER')
    
    def generate_sales_report():
        """生成銷售報(bào)告,返回報(bào)告文件路徑"""
        try:
            # 假設(shè)這里連接數(shù)據(jù)庫,提取數(shù)據(jù)并生成報(bào)告
            # 實(shí)際項(xiàng)目中,這里會(huì)有數(shù)據(jù)庫查詢、數(shù)據(jù)處理、生成圖表等邏輯
            
            # 示例:創(chuàng)建一個(gè)簡(jiǎn)單的銷售數(shù)據(jù)Excel
            df = pd.DataFrame({
                '產(chǎn)品': ['產(chǎn)品A', '產(chǎn)品B', '產(chǎn)品C', '產(chǎn)品D'],
                '銷量': [123, 456, 789, 321],
                '收入': [12300, 45600, 78900, 32100]
            })
            
            today = datetime.now().strftime('%Y%m%d')
            report_path = f'sales_report_{today}.xlsx'
            df.to_excel(report_path, index=False)
            
            return report_path
        except Exception as e:
            logging.error(f"生成報(bào)告失敗: {str(e)}")
            return None
    
    def send_sales_report(max_retries=3):
        """發(fā)送銷售報(bào)告郵件,帶附件和HTML格式內(nèi)容"""
        logging.info("開始準(zhǔn)備發(fā)送銷售報(bào)告")
        
        # 生成報(bào)告
        report_path = generate_sales_report()
        if not report_path:
            logging.error("無法生成報(bào)告,取消發(fā)送郵件")
            return False
        
        # 獲取今天的日期
        today = datetime.now().strftime('%Y-%m-%d')
        yesterday = (datetime.now() - pd.Timedelta(days=1)).strftime('%Y-%m-%d')
        
        # 創(chuàng)建郵件
        message = MIMEMultipart()
        message['From'] = Header('銷售數(shù)據(jù)分析系統(tǒng)', 'utf-8')
        message['To'] = Header('銷售團(tuán)隊(duì)', 'utf-8')
        message['Subject'] = Header(f'{yesterday} 銷售數(shù)據(jù)報(bào)告', 'utf-8')
        
        # HTML郵件內(nèi)容
        html_content = f"""
        <html>
        <body>
            <h2 style="color:#003366;">銷售數(shù)據(jù)日?qǐng)?bào) - {yesterday}</h2>
            <p>各位銷售經(jīng)理:</p>
            <p>附件是 <strong>{yesterday}</strong> 的銷售數(shù)據(jù)報(bào)表,主要數(shù)據(jù)如下:</p>
            <table border="1" cellpadding="5" cellspacing="0" style="border-collapse:collapse">
                <tr style="background-color:#f2f2f2">
                    <th>指標(biāo)</th>
                    <th>數(shù)值</th>
                    <th>與上周同比</th>
                </tr>
                <tr>
                    <td>總銷售額</td>
                    <td style="text-align:right">¥169,900</td>
                    <td style="color:green">↑ 12.3%</td>
                </tr>
                <tr>
                    <td>訂單數(shù)</td>
                    <td style="text-align:right">1,689</td>
                    <td style="color:green">↑ 8.7%</td>
                </tr>
                <tr>
                    <td>平均客單價(jià)</td>
                    <td style="text-align:right">¥100.59</td>
                    <td style="color:green">↑ 3.3%</td>
                </tr>
            </table>
            <p>詳細(xì)數(shù)據(jù)請(qǐng)查看附件,如有問題請(qǐng)及時(shí)聯(lián)系數(shù)據(jù)組。</p>
            <p style="color:#888888">此郵件由系統(tǒng)自動(dòng)發(fā)送,請(qǐng)勿回復(fù)。</p>
        </body>
        </html>
        """
        
        # 添加HTML內(nèi)容
        message.attach(MIMEText(html_content, 'html', 'utf-8'))
        
        # 添加Excel附件
        with open(report_path, 'rb') as f:
            attachment = MIMEApplication(f.read())
            attachment_name = os.path.basename(report_path)
            attachment.add_header('Content-Disposition', 'attachment', filename=attachment_name)
            message.attach(attachment)
        
        # 發(fā)送郵件,帶重試機(jī)制
        receivers = ['sales_manager@example.com', 'director@example.com', 'ceo@example.com']
        
        retry_count = 0
        while retry_count < max_retries:
            try:
                smtp_obj = smtplib.SMTP_SSL(SMTP_SERVER, 465)
                smtp_obj.login(EMAIL_USER, EMAIL_PASSWORD)
                smtp_obj.sendmail(EMAIL_USER, receivers, message.as_string())
                smtp_obj.quit()
                
                logging.info(f"銷售報(bào)告郵件成功發(fā)送給 {', '.join(receivers)}")
                
                # 刪除臨時(shí)生成的報(bào)表文件
                os.remove(report_path)
                return True
                
            except Exception as e:
                retry_count += 1
                logging.warning(f"發(fā)送失敗 (嘗試 {retry_count}/{max_retries}): {str(e)}")
                time.sleep(5)  # 等待5秒后重試
        
        logging.error(f"發(fā)送銷售報(bào)告郵件失敗,已達(dá)到最大重試次數(shù)")
        return False
    
    # 設(shè)置定時(shí)任務(wù):每天早上8:30發(fā)送報(bào)告
    def scheduled_job():
        logging.info("執(zhí)行定時(shí)任務(wù):發(fā)送每日銷售報(bào)告")
        send_sales_report()
    
    # 如果直接運(yùn)行腳本,執(zhí)行測(cè)試發(fā)送
    if __name__ == "__main__":
        # 設(shè)置定時(shí)任務(wù)
        schedule.every().day.at("08:30").do(scheduled_job)
        
        print("郵件發(fā)送系統(tǒng)已啟動(dòng),將在每天 08:30 發(fā)送銷售報(bào)告")
        print("按 Ctrl+C 停止程序")
        
        try:
            # 立即發(fā)送一次作為測(cè)試
            print("正在發(fā)送測(cè)試郵件...")
            result = send_sales_report()
            print("測(cè)試郵件發(fā)送結(jié)果:", "成功" if result else "失敗")
            
            # 持續(xù)運(yùn)行,等待定時(shí)任務(wù)
            while True:
                schedule.run_pending()
                time.sleep(60)
        except KeyboardInterrupt:
            print("程序已停止")

這個(gè)完整示例包含了自動(dòng)生成報(bào)表、格式化HTML郵件內(nèi)容、添加附件、錯(cuò)誤重試、日志記錄等功能,可以直接部署到服務(wù)器上定時(shí)運(yùn)行。我相信每天手動(dòng)發(fā)數(shù)據(jù)報(bào)表的同學(xué)看到這個(gè)得笑出聲,太多痛點(diǎn)一次性解決了。

這種自動(dòng)化郵件系統(tǒng)可以應(yīng)用在很多場(chǎng)景:

  • 定期發(fā)送各種業(yè)務(wù)數(shù)據(jù)報(bào)表
  • 系統(tǒng)監(jiān)控告警通知
  • 客戶關(guān)懷自動(dòng)郵件
  • 訂單狀態(tài)通知
  • 運(yùn)營(yíng)數(shù)據(jù)定時(shí)播報(bào)

根據(jù)實(shí)際需求稍作修改就能派上用場(chǎng)。對(duì)許多初級(jí)程序員來說,Python自動(dòng)發(fā)郵件可能是接觸自動(dòng)化的第一步,掌握了這個(gè)技能,你就會(huì)開始思考:“還有什么工作是可以用程序自動(dòng)完成的?”

這個(gè)思路才是最值錢的。

從這個(gè)小小的郵件自動(dòng)化開始,漸漸把團(tuán)隊(duì)里各種重復(fù)性工作都自動(dòng)化了:數(shù)據(jù)采集、報(bào)表生成、監(jiān)控告警、異常檢測(cè)。最后整個(gè)團(tuán)隊(duì)的效率提升了3倍多,我也因此拿到了晉升機(jī)會(huì)。寫代碼最大的樂趣之一,就是用技術(shù)解決實(shí)際問題,讓自己和同事的工作更輕松。

以上就是基于Python+smtplib實(shí)現(xiàn)郵件自動(dòng)發(fā)送功能的詳細(xì)內(nèi)容,更多關(guān)于Python smtplib郵件自動(dòng)發(fā)送的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何讓Python在HTML中運(yùn)行

    如何讓Python在HTML中運(yùn)行

    這個(gè)名為PyScript的框架,其核心目標(biāo)是為開發(fā)者提供在標(biāo)準(zhǔn)HTML中嵌入Python代碼的能力,使用?Python調(diào)用JavaScript函數(shù)庫,并以此實(shí)現(xiàn)利用Python創(chuàng)建Web應(yīng)用的功能,本文給大家介紹Python?HTML運(yùn)行的案例解析,感興趣的朋友一起看看吧
    2022-05-05
  • 用Python復(fù)現(xiàn)二戰(zhàn)德軍enigma密碼機(jī)

    用Python復(fù)現(xiàn)二戰(zhàn)德軍enigma密碼機(jī)

    大家好,本篇文章主要講的是用Python復(fù)現(xiàn)二戰(zhàn)德軍enigma密碼機(jī),感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • python進(jìn)程池和線程池的區(qū)別

    python進(jìn)程池和線程池的區(qū)別

    本文主要介紹了python進(jìn)程池和線程池的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-12-12
  • Python中canmatrix解析dbc文件應(yīng)用常見示例

    Python中canmatrix解析dbc文件應(yīng)用常見示例

    DBC是一種用于描述CAN網(wǎng)絡(luò)協(xié)議的文件格式,Python作為一種流行的編程語言,也提供了解析DBC文件的相關(guān)類庫,這篇文章主要給大家介紹了關(guān)于Python中canmatrix解析dbc文件的相關(guān)資料,需要的朋友可以參考下
    2024-08-08
  • Django 外鍵的使用方法詳解

    Django 外鍵的使用方法詳解

    這篇文章主要介紹了Django 外鍵的使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • pandas.DataFrame Series排序的使用(sort_values,sort_index)

    pandas.DataFrame Series排序的使用(sort_values,sort_index)

    本文主要介紹了pandas.DataFrame Series排序的使用(sort_values,sort_index),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Python入門教程(二十六)Python的模塊

    Python入門教程(二十六)Python的模塊

    這篇文章主要介紹了Python入門教程(二十六)Python的模塊,Python是一門非常強(qiáng)大好用的語言,也有著易上手的特性,本文為入門教程,需要的朋友可以參考下
    2023-04-04
  • Python?切片為什么不會(huì)索引越界?

    Python?切片為什么不會(huì)索引越界?

    這篇文章主要介紹了Python?切片為什么不會(huì)索引越界?切片(slice)是?Python?中一種很有特色的特性,在正式開始之前,我們先來從關(guān)于切片的相關(guān)知識(shí)開始介紹,感興趣的小伙伴一起參考參考呀</P><P>
    2021-12-12
  • python中字符串類型json操作的注意事項(xiàng)

    python中字符串類型json操作的注意事項(xiàng)

    這篇文章主要給大家介紹了python中字符串類型json操作的一些注意事項(xiàng),文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-05-05
  • python中wheel的用法整理

    python中wheel的用法整理

    在本篇文章里小編給大家整理的是關(guān)于python中wheel的用法整理內(nèi)容,需要的朋友們可以跟著學(xué)習(xí)下。
    2020-06-06

最新評(píng)論

淄博市| 榆林市| 安西县| 宁城县| 方山县| 民勤县| 呈贡县| 铅山县| 太和县| 南郑县| 晋江市| 景谷| 申扎县| 香格里拉县| 潮州市| 石楼县| 安陆市| 永宁县| 南昌市| 锡林浩特市| 漳浦县| 洪江市| 仲巴县| 东乌珠穆沁旗| 邮箱| 南丰县| 岳普湖县| 鞍山市| 崇明县| 名山县| 永昌县| 塔河县| 淮安市| 仙游县| 那曲县| 福贡县| 红河县| 新晃| 台中县| 六安市| 古交市|