Python利用SMTP發(fā)送郵件的常見問題與解決方案詳解
引言
在自動(dòng)化辦公和系統(tǒng)開發(fā)中,郵件發(fā)送功能是常見的需求。無論是發(fā)送通知、報(bào)告,還是傳輸文件,Python的smtplib和email庫(kù)提供了便捷的實(shí)現(xiàn)方式。然而,在實(shí)際開發(fā)中,開發(fā)者常會(huì)遇到各種SMTP錯(cuò)誤,如(-1, b'\x00\x00\x00')、AttributeError('characters_written')等,這些問題往往讓人束手無策。
本文將從實(shí)際案例出發(fā),分析常見的SMTP錯(cuò)誤,并提供完整的解決方案,幫助開發(fā)者快速定位和解決問題。同時(shí),我們還會(huì)優(yōu)化代碼,使其更健壯、更易于維護(hù)。
1. 問題背景
在Python中,使用smtplib發(fā)送郵件時(shí),通常會(huì)遇到兩類問題:
- 連接與認(rèn)證問題(如授權(quán)碼錯(cuò)誤、SMTP服務(wù)器拒絕連接)
- 數(shù)據(jù)編碼與協(xié)議問題(如SSL/TLS握手失敗、附件編碼錯(cuò)誤)
以下是一個(gè)典型的郵件發(fā)送代碼,它可能會(huì)觸發(fā)上述錯(cuò)誤:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import os
import logging
logger = logging.getLogger(__name__)
def send_email_with_attachment(filepath, receiver_email):
sender_email = "your_email@qq.com"
password = "your_authorization_code" # QQ郵箱授權(quán)碼
smtp_server = "smtp.qq.com"
smtp_port = 465 # QQ郵箱SSL端口
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = "文件處理結(jié)果通知"
# 郵件正文
msg.attach(MIMEText("請(qǐng)查收附件", 'plain'))
# 添加附件
try:
with open(filepath, "rb") as f:
part = MIMEApplication(f.read(), Name=os.path.basename(filepath))
part['Content-Disposition'] = f'attachment; filename="{os.path.basename(filepath)}"'
msg.attach(part)
except FileNotFoundError:
logger.error("附件文件不存在")
return False
# 發(fā)送郵件
try:
with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, msg.as_string())
return True
except Exception as e:
logger.error(f"發(fā)送郵件失敗: {e}", exc_info=True)
return False
運(yùn)行此代碼時(shí),可能會(huì)遇到以下錯(cuò)誤:
2. 常見錯(cuò)誤及解決方案
錯(cuò)誤1:SMTPResponseException: (-1, b'\x00\x00\x00')
錯(cuò)誤原因
- 授權(quán)碼錯(cuò)誤或已過期。
- SMTP服務(wù)器(如QQ郵箱)檢測(cè)到異常登錄,拒絕連接。
- 網(wǎng)絡(luò)或防火墻攔截了SMTP請(qǐng)求。
解決方案
1.檢查授權(quán)碼
登錄QQ郵箱 → 設(shè)置 → 賬戶 → 生成新的授權(quán)碼,并替換代碼中的password。
2.更換SMTP端口(推薦使用587+TLS)
QQ郵箱支持465(SSL)和587(TLS),后者更穩(wěn)定:
smtp_port = 587 # 改用TLS端口
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls() # 啟用TLS加密
server.login(sender_email, password)
3.檢查網(wǎng)絡(luò)環(huán)境
關(guān)閉防火墻或切換網(wǎng)絡(luò)(如使用手機(jī)熱點(diǎn)測(cè)試)。
錯(cuò)誤2:AttributeError('characters_written')
錯(cuò)誤原因
- Python 3.10+ 與 SMTP_SSL 的兼容性問題。
- SSL/TLS握手失敗,可能是由于OpenSSL版本不匹配。
解決方案
1.改用starttls()(推薦)
避免使用SMTP_SSL,改用SMTP + starttls():
with smtplib.SMTP(smtp_server, 587) as server:
server.starttls() # 顯式啟用TLS
server.login(sender_email, password)
2.降級(jí)Python或升級(jí)依賴庫(kù)
如果仍報(bào)錯(cuò),嘗試降級(jí)到Python 3.9,或更新pyopenssl:
pip install --upgrade pyopenssl
3. 優(yōu)化后的郵件發(fā)送代碼
結(jié)合上述解決方案,優(yōu)化后的代碼如下:
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
import os
import logging
import base64
logger = logging.getLogger(__name__)
def send_email_with_attachment(filepath, receiver_email):
"""發(fā)送帶附件的郵件(支持QQ郵箱)"""
sender_email = "your_email@qq.com"
password = "your_authorization_code" # 替換為最新授權(quán)碼
smtp_server = "smtp.qq.com"
smtp_port = 587 # 使用TLS端口
# 檢查附件是否存在
if not os.path.exists(filepath):
logger.error(f"附件文件不存在: {filepath}")
return False
# 創(chuàng)建郵件內(nèi)容
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = "文件處理結(jié)果通知"
msg.attach(MIMEText("請(qǐng)查收附件", 'plain'))
# 添加附件
try:
with open(filepath, "rb") as f:
part = MIMEApplication(f.read(), Name=os.path.basename(filepath))
part['Content-Disposition'] = f'attachment; filename="{os.path.basename(filepath)}"'
msg.attach(part)
except Exception as e:
logger.error(f"附件處理失敗: {e}")
return False
# 發(fā)送郵件(使用TLS)
try:
with smtplib.SMTP(smtp_server, smtp_port) as server:
server.starttls() # 啟用TLS加密
server.login(sender_email, password)
server.sendmail(sender_email, receiver_email, msg.as_string())
logger.info("郵件發(fā)送成功")
return True
except smtplib.SMTPException as e:
logger.error(f"SMTP錯(cuò)誤: {e}")
except Exception as e:
logger.error(f"未知錯(cuò)誤: {e}", exc_info=True)
return False
優(yōu)化點(diǎn)
1.更健壯的異常處理
區(qū)分SMTPException和其他異常,便于排查問題。
2.附件預(yù)檢查
發(fā)送前驗(yàn)證附件是否存在。
3.日志記錄
使用logging記錄詳細(xì)錯(cuò)誤信息。
4. SMTP調(diào)試技巧
手動(dòng)測(cè)試SMTP連接
使用openssl命令行工具測(cè)試SMTP服務(wù)是否可用:
openssl s_client -connect smtp.qq.com:587 -starttls smtp -crlf
輸入以下命令(替換為你的郵箱和授權(quán)碼):
EHLO test
AUTH LOGIN
<base64編碼的郵箱> # 示例:echo -n "your_email@qq.com" | base64
<base64編碼的授權(quán)碼>
QUIT
如果返回235 Authentication successful,說明SMTP配置正確。
檢查防火墻
在Windows上,運(yùn)行以下命令放行SMTP端口:
netsh advfirewall firewall add rule name="SMTP" dir=in action=allow protocol=TCP localport=587
5. 總結(jié)與最佳實(shí)踐
常見問題總結(jié)
| 錯(cuò)誤 | 原因 | 解決方案 |
|---|---|---|
(-1, b'\x00\x00\x00') | 授權(quán)碼錯(cuò)誤/SMTP拒絕 | 更新授權(quán)碼,改用starttls() |
AttributeError('characters_written') | Python 3.10+兼容性問題 | 降級(jí)Python或改用SMTP + starttls() |
SSL: WRONG_VERSION_NUMBER | SSL/TLS配置錯(cuò)誤 | 使用starttls() + 端口587 |
最佳實(shí)踐
- 使用TLS(端口587)代替SSL(端口465),兼容性更好。
- 定期更新授權(quán)碼,避免因過期導(dǎo)致發(fā)送失敗。
- 添加日志記錄,便于排查問題。
- 手動(dòng)測(cè)試SMTP連接,確保服務(wù)器可用。
通過本文的分析和優(yōu)化,你應(yīng)該能夠解決大多數(shù)Python郵件發(fā)送問題。如果你的場(chǎng)景涉及更復(fù)雜的需求(如批量發(fā)送、HTML郵件),可以進(jìn)一步擴(kuò)展代碼邏輯。
到此這篇關(guān)于Python利用SMTP發(fā)送郵件的常見問題與解決方案詳解的文章就介紹到這了,更多相關(guān)Python SMTP發(fā)送郵件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決python matplotlib imshow無法顯示的問題
今天小編就為大家分享一篇解決python matplotlib imshow無法顯示的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-05-05
python下10個(gè)簡(jiǎn)單實(shí)例代碼
最近學(xué)python比較順手,找到感覺了,所以,我想把我用來練習(xí)的實(shí)例題目分享出來,有興趣的朋友可以關(guān)注一下。 文章分為10篇,每篇10題,共100道實(shí)例。后續(xù)如果需要可以增加2017-11-11
Python使用type動(dòng)態(tài)創(chuàng)建類操作示例
這篇文章主要介紹了Python使用type動(dòng)態(tài)創(chuàng)建類操作,結(jié)合實(shí)例形式詳細(xì)分析了Python使用type動(dòng)態(tài)創(chuàng)建類的具體原理、實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-02-02
Python機(jī)器學(xué)習(xí)實(shí)戰(zhàn)之k-近鄰算法的實(shí)現(xiàn)
k-近鄰算法采用測(cè)量不同特征值之間的距離方法進(jìn)行分類。這篇文章主要為大家介紹了如何通過python實(shí)現(xiàn)K近鄰算法,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-11-11

