基于Python編寫自動化郵件發(fā)送程序(進階版)
在數(shù)字化時代,自動化郵件發(fā)送功能已成為企業(yè)和個人提升工作效率的重要工具。據(jù)統(tǒng)計,全球每天發(fā)送的商業(yè)郵件超過30億封,其中約40%是通過自動化系統(tǒng)發(fā)送的。這種功能被廣泛應(yīng)用于多種場景:在日常辦公中用于自動發(fā)送會議通知和日程提醒;在電商領(lǐng)域用于訂單確認和物流跟蹤;在營銷推廣中用于EDM郵件營銷和客戶關(guān)懷;在IT系統(tǒng)中用于發(fā)送告警通知和驗證碼等。
Python憑借其簡潔優(yōu)雅的語法和強大的生態(tài)系統(tǒng),成為實現(xiàn)郵件自動化的首選編程語言。其標準庫中的smtplib和email模塊提供了完整的郵件處理功能,而第三方庫如yagmail更是將發(fā)送過程簡化到極致。Python的跨平臺特性使其可以在Windows、Linux和macOS等不同操作系統(tǒng)上穩(wěn)定運行,且能輕松與其他系統(tǒng)集成。
本教程將詳細介紹如何用Python編寫一個完整的自動化郵件發(fā)送程序,包括:
- 配置SMTP服務(wù)器參數(shù)
- 構(gòu)建郵件內(nèi)容(支持純文本和HTML格式)
- 添加附件(如圖片、文檔等)
- 實現(xiàn)批量發(fā)送功能
- 處理發(fā)送異常和日志記錄
- 部署到生產(chǎn)環(huán)境的注意事項
我們將通過實際案例演示,從基礎(chǔ)的單封郵件發(fā)送到高級的定時批量發(fā)送功能,幫助讀者掌握完整的郵件自動化解決方案。
理解SMTP協(xié)議基礎(chǔ)
SMTP(Simple Mail Transfer Protocol)是用于發(fā)送電子郵件的標準協(xié)議。Python通過smtplib庫提供SMTP協(xié)議支持。發(fā)送郵件的基本流程涉及三個關(guān)鍵環(huán)節(jié):建立SMTP連接、進行身份驗證、構(gòu)造并發(fā)送郵件內(nèi)容。
電子郵件通常由頭部(From/To/Subject等)和正文組成,可能包含純文本、HTML內(nèi)容或附件。MIME(Multipurpose Internet Mail Extensions)標準用于擴展郵件格式支持。
配置開發(fā)環(huán)境
需要安裝Python 3.6及以上版本。通過以下命令安裝必要依賴庫:
pip install secure-smtplib email-validator
建議使用虛擬環(huán)境隔離項目依賴:
python -m venv email_env source email_env/bin/activate # Linux/macOS email_env\Scripts\activate # Windows
構(gòu)建郵件發(fā)送函數(shù)核心邏輯
創(chuàng)建send_email.py文件,導(dǎo)入基礎(chǔ)模塊:
import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.utils import formatdate import getpass
初始化SMTP連接函數(shù):
def connect_smtp_server(smtp_server, port, username, password):
try:
server = smtplib.SMTP(smtp_server, port)
server.starttls()
server.login(username, password)
return server
except Exception as e:
print(f"連接SMTP服務(wù)器失敗: {str(e)}")
raise
郵件內(nèi)容構(gòu)造器:
def build_email(sender, receiver, subject, body, body_type='plain'):
message = MIMEMultipart()
message['From'] = sender
message['To'] = receiver
message['Date'] = formatdate(localtime=True)
message['Subject'] = subject
if body_type == 'html':
content = MIMEText(body, 'html')
else:
content = MIMEText(body, 'plain')
message.attach(content)
return message
實現(xiàn)完整發(fā)送流程
主發(fā)送函數(shù)整合所有組件:
def send_email(smtp_config, email_content):
try:
server = connect_smtp_server(
smtp_config['server'],
smtp_config['port'],
smtp_config['username'],
smtp_config['password']
)
message = build_email(
email_content['sender'],
email_content['receiver'],
email_content['subject'],
email_content['body'],
email_content.get('body_type', 'plain')
)
server.sendmail(
email_content['sender'],
email_content['receiver'],
message.as_string()
)
server.quit()
print("郵件發(fā)送成功")
return True
except Exception as e:
print(f"郵件發(fā)送失敗: {str(e)}")
return False
添加附件支持功能
擴展郵件構(gòu)造器支持附件:
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email import encoders
import os
def add_attachment(message, file_path):
with open(file_path, "rb") as attachment:
part = MIMEBase('application', 'octet-stream')
part.set_payload(attachment.read())
encoders.encode_base64(part)
part.add_header(
'Content-Disposition',
f'attachment; filename={os.path.basename(file_path)}'
)
message.attach(part)
更新后的郵件構(gòu)造器:
def build_email(sender, receiver, subject, body, attachments=None, body_type='plain'):
message = MIMEMultipart()
# ... 原有頭部設(shè)置代碼 ...
if attachments:
for file_path in attachments:
if os.path.exists(file_path):
add_attachment(message, file_path)
else:
print(f"警告:附件文件不存在 {file_path}")
return message
實現(xiàn)HTML郵件模板
創(chuàng)建HTML模板文件template.html:
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; }
.header { color: #2c3e50; }
.content { margin: 20px 0; }
.footer { color: #7f8c8d; font-size: 0.8em; }
</style>
</head>
<body>
<h1 class="header">${HEADER}</h1>
<div class="content">${CONTENT}</div>
<div class="footer">自動發(fā)送于 ${DATE}</div>
</body>
</html>
添加模板處理函數(shù):
from string import Template
import datetime
def render_template(template_file, **kwargs):
with open(template_file, 'r') as f:
template = Template(f.read())
if 'DATE' not in kwargs:
kwargs['DATE'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
return template.substitute(**kwargs)
安全增強措施
實現(xiàn)密碼加密存儲:
from cryptography.fernet import Fernet
import base64
import json
class CredentialManager:
def __init__(self, key_file='.key'):
self.key = self._load_or_generate_key(key_file)
def _load_or_generate_key(self, key_file):
if os.path.exists(key_file):
with open(key_file, 'rb') as f:
return f.read()
else:
key = Fernet.generate_key()
with open(key_file, 'wb') as f:
f.write(key)
return key
def save_credentials(self, config_file, credentials):
cipher = Fernet(self.key)
encrypted = cipher.encrypt(json.dumps(credentials).encode())
with open(config_file, 'wb') as f:
f.write(encrypted)
def load_credentials(self, config_file):
cipher = Fernet(self.key)
with open(config_file, 'rb') as f:
encrypted = f.read()
return json.loads(cipher.decrypt(encrypted).decode())
完整實現(xiàn)代碼
# email_sender.py
import smtplib
import os
import json
import getpass
import datetime
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email import encoders
from email.utils import formatdate
from string import Template
from cryptography.fernet import Fernet
import base64
class EmailSender:
def __init__(self, config_file='config.json', key_file='.key'):
self.config_file = config_file
self.cred_manager = CredentialManager(key_file)
if not os.path.exists(config_file):
self._setup_config()
self.config = self.cred_manager.load_credentials(config_file)
def _setup_config(self):
print("首次使用需要進行配置")
smtp_server = input("SMTP服務(wù)器地址 (如smtp.example.com): ")
port = int(input("端口號 (587 for TLS): "))
username = input("郵箱地址: ")
password = getpass.getpass("郵箱密碼/授權(quán)碼: ")
credentials = {
'smtp_server': smtp_server,
'port': port,
'username': username,
'password': password
}
self.cred_manager.save_credentials(self.config_file, credentials)
def connect_smtp(self):
try:
server = smtplib.SMTP(self.config['smtp_server'], self.config['port'])
server.starttls()
server.login(self.config['username'], self.config['password'])
return server
except Exception as e:
raise Exception(f"SMTP連接失敗: {str(e)}")
def build_message(self, to, subject, body, body_type='plain', attachments=None, cc=None, bcc=None):
msg = MIMEMultipart()
msg['From'] = self.config['username']
msg['To'] = to
msg['Subject'] = subject
msg['Date'] = formatdate(localtime=True)
if cc:
msg['Cc'] = cc
if bcc:
msg['Bcc'] = bcc
if body_type == 'html':
msg.attach(MIMEText(body, 'html'))
else:
msg.attach(MIMEText(body, 'plain'))
if attachments:
for file_path in attachments:
if os.path.exists(file_path):
self._add_attachment(msg, file_path)
else:
print(f"附件不存在: {file_path}")
return msg
def _add_attachment(self, message, file_path):
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',
f'attachment; filename="{os.path.basename(file_path)}"'
)
message.attach(part)
def send(self, to, subject, body, body_type='plain', attachments=None, cc=None, bcc=None):
try:
server = self.connect_smtp()
recipients = [to]
if cc:
recipients += cc.split(',')
if bcc:
recipients += bcc.split(',')
msg = self.build_message(to, subject, body, body_type, attachments, cc, bcc)
server.sendmail(self.config['username'], recipients, msg.as_string())
server.quit()
return True
except Exception as e:
print(f"發(fā)送失敗: {str(e)}")
return False
class CredentialManager:
def __init__(self, key_file='.key'):
self.key = self._load_or_generate_key(key_file)
def _load_or_generate_key(self, key_file):
if os.path.exists(key_file):
with open(key_file, 'rb') as f:
return f.read()
else:
key = Fernet.generate_key()
with open(key_file, 'wb') as f:
f.write(key)
return key
def save_credentials(self, config_file, credentials):
cipher = Fernet(self.key)
encrypted = cipher.encrypt(json.dumps(credentials).encode())
with open(config_file, 'wb') as f:
f.write(encrypted)
def load_credentials(self, config_file):
cipher = Fernet(self.key)
with open(config_file, 'rb') as f:
encrypted = f.read()
return json.loads(cipher.decrypt(encrypted).decode())
def render_template(template_file, **kwargs):
with open(template_file, 'r') as f:
template = Template(f.read())
if 'DATE' not in kwargs:
kwargs['DATE'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
return template.substitute(**kwargs)
if __name__ == "__main__":
sender = EmailSender()
# 示例1:發(fā)送純文本郵件
sender.send(
to="recipient@example.com",
subject="測試純文本郵件",
body="這是一封測試郵件的正文內(nèi)容。"
)
# 示例2:發(fā)送帶附件的HTML郵件
html_content = """
<h1>HTML郵件測試</h1>
<p>這是一封<strong>HTML格式</strong>的測試郵件。</p>
<p>當(dāng)前時間:{}</p>
""".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
sender.send(
to="recipient@example.com",
subject="測試HTML郵件",
body=html_content,
body_type='html',
attachments=['report.pdf', 'data.xlsx']
)
# 示例3:使用模板發(fā)送郵件
template_vars = {
'HEADER': '季度報告',
'CONTENT': '請查收附件中的季度財務(wù)報告。'
}
rendered = render_template('template.html', **template_vars)
sender.send(
to="manager@example.com",
subject="季度財務(wù)報告",
body=rendered,
body_type='html',
attachments=['quarterly_report.pdf']
)
項目結(jié)構(gòu)說明
完整項目應(yīng)包含以下文件:
/email_project
│── email_sender.py # 主程序
│── config.json # 加密的配置文件
│── .key # 加密密鑰
│── template.html # HTML模板
├── attachments/ # 附件目錄
│ ├── report.pdf
│ └── data.xlsx
└── requirements.txt # 依賴列表
requirements.txt內(nèi)容:
secure-smtplib==1.0.0
cryptography==39.0.1
部署與使用指南
1.首次運行會自動引導(dǎo)配置SMTP參數(shù)
2.支持三種發(fā)送模式:
- 純文本郵件
- HTML格式郵件
- 帶附件郵件
3.密碼等敏感信息采用AES加密存儲
4.可通過繼承EmailSender類擴展功能
安全注意事項
- 不要將.key文件提交到版本控制
- 建議使用應(yīng)用專用密碼而非賬戶密碼
- 附件發(fā)送前應(yīng)進行病毒掃描
- 生產(chǎn)環(huán)境建議添加發(fā)送頻率限制
通過本教程,可以構(gòu)建一個功能完備的企業(yè)級郵件自動發(fā)送系統(tǒng)。根據(jù)實際需求,可進一步擴展郵件隊列、發(fā)送狀態(tài)跟蹤等功能。
到此這篇關(guān)于基于Python編寫自動化郵件發(fā)送程序(進階版)的文章就介紹到這了,更多相關(guān)Python自動化郵件發(fā)送內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Pygame中Pygame模塊的大戰(zhàn)外星人實戰(zhàn)
本文主要介紹了基于Pygame中Pygame模塊的大戰(zhàn)外星人實戰(zhàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12
教你用python3根據(jù)關(guān)鍵詞爬取百度百科的內(nèi)容
這篇文章介紹的是利用python3根據(jù)關(guān)鍵詞爬取百度百科的內(nèi)容,注意本文用的是python3版本以及根據(jù)關(guān)鍵詞爬取,爬取也只是單純的爬網(wǎng)頁信息,有需要的可以參考借鑒。2016-08-08
Python讀取Excel數(shù)據(jù)實現(xiàn)批量生成PPT
我們常常面臨著大量的重復(fù)性工作,通過人工方式處理往往耗時耗力易出錯。而Python在辦公自動化方面具有天然優(yōu)勢。本文將利用讀取Excel數(shù)據(jù)并實現(xiàn)批量生成PPT,需要的可以參考一下2022-05-05
Python整合SQLite搭建一個輕量級賬本應(yīng)用
這篇文章為大家詳細主要介紹了如何使用Python整合SQLite搭建一個輕量級賬本應(yīng)用,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-07-07
python使用多線程查詢數(shù)據(jù)庫的實現(xiàn)示例
這篇文章主要介紹了python使用多線程查詢數(shù)據(jù)庫的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
java中兩個byte數(shù)組實現(xiàn)合并的示例
今天小編就為大家分享一篇java中兩個byte數(shù)組實現(xiàn)合并的示例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05
基于python OpenCV實現(xiàn)動態(tài)人臉檢測
這篇文章主要為大家詳細介紹了基于python OpenCV實現(xiàn)動態(tài)人臉檢測,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05

