手把手用Python实现邮件自动发送:从单发到批量群发的完整指南

怎么说呢,Python发邮件这事儿原理其实挺简单的:用smtplib库连上邮件服务器的SMTP端口,再用email库把邮件内容(标题、正文、附件啥的)组装好,最后一把扔出去。就像你手动写信、贴邮票、扔邮筒一样,只不过这些活儿全由代码干了。我用的是Python 3.8,标准库就够了,不用装第三方包。

先来看第一步:准备邮箱和开启SMTP服务。这是最容易卡住的地方,我第一次就栽了。我拿QQ邮箱举例(网易、163、Gmail也都差不多)。登录网页版QQ邮箱 → 设置 → 账户 → 找到“POP3/IMAP/SMTP服务”,把SMTP服务开了。开的时候会生成一个授权码——注意!这个不是你的登录密码,别搞混了。记下SMTP服务器地址:QQ的是smtp.qq.com,端口465(SSL)或587(TLS);网易的是smtp.163.com,端口465;Gmail的是smtp.gmail.com,端口587(还得开两步验证+应用专用密码)。

踩坑提醒:千万别直接用邮箱密码登录SMTP,必须用授权码!我当时看到“Authentication failed”就傻眼了,后来发现是密码不对,气得差点把电脑砸了。

好了,第二步:写一个最简单的发送函数。先来个“Hello World”版本——只发纯文本邮件,能发出去就行。

python
import smtplib
from email.mime.text import MIMEText

def send_simple_email(sender_email, sender_password, receiver_email, subject, body):
# 构造邮件内容
msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver_email

try:
# 连接QQ邮箱SMTP服务器(SSL模式)
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender_email, sender_password)
server.sendmail(sender_email, [receiver_email], msg.as_string())
server.quit()
print(f"邮件发送成功 → {receiver_email}")
except Exception as e:
print(f"发送失败: {e}")

使用示例

send_simple_email(
sender_email='your_email@qq.com',
sender_password='your_authorization_code', # 授权码!
receiver_email='friend@example.com',
subject='测试邮件标题',
body='这是Python自动发送的邮件正文。'
)
`

我用SMTP_SSLSMTP()starttls()稳多了,至少QQ邮箱上从来没断过。还有就是一定要加try-except,不然脚本崩了你都不知道为啥——别问我怎么知道的。

对了,纯文本太素了吧?我一般喜欢往邮件里塞HTML和附件,这时候发现MIMEText就不够用了,得换MIMEMultipart。下面这个函数就是干这个的,好用得很:

`python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os

def send_rich_email(sender_email, sender_password, receiver_email, subject, html_body, attachment_path=None):
# 创建邮件容器
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver_email

# 添加HTML正文
html_part = MIMEText(html_body, 'html', 'utf-8')
msg.attach(html_part)

# 添加附件(如果有)
if attachment_path and os.path.exists(attachment_path):
with open(attachment_path, 'rb') as f:
attachment = MIMEBase('application', 'octet-stream')
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header(
'Content-Disposition',
'attachment',
filename=('utf-8', '', os.path.basename(attachment_path))
)
msg.attach(attachment)

# 发送
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender_email, sender_password)
server.sendmail(sender_email, [receiver_email], msg.as_string())
server.quit()
print(f"邮件发送成功 → {receiver_email}")
except Exception as e:
print(f"发送失败: {e}")

使用示例

html = """

你好,这是一封HTML邮件

你可以在正文里放表格、图片、链接。

Python官网
"""
send_rich_email(
sender_email='your_email@qq.com',
sender_password='your_authorization_code',
receiver_email='friend@example.com',
subject='带HTML和附件的邮件',
html_body=html,
attachment_path='report.pdf' # 可选
)
`

踩过的坑:附件文件名有中文的时候,如果不指定‘utf-8’编码,收件人那边就是一堆乱码。上面代码里filename=(‘utf-8’, ”, os.path.basename(attachment_path))就是专门解决这个的。我一开始没写,被同事骂了好几次。

_

一张对比图:左边是纯文本邮件,右边是带表格和链接的HTML邮件,直观展示差异

_

解决方案其实很简单:分批发送,再加上随机延时。下面是我写的函数,我用了半年没出过问题:

`python
import time
import random
import smtplib
from email.mime.text import MIMEText

def batch_send_emails(sender_email, sender_password, recipients_list, subject, body, batch_size=5, delay_range=(5, 15)):
total = len(recipients_list)
sent_count = 0
fail_count = 0

for i in range(0, total, batch_size):
batch = recipients_list[i:i+batch_size]
print(f"正在发送第 {i+1}-{min(i+batch_size, total)} 封...")

for receiver in batch:
try:
server = smtplib.SMTP_SSL('smtp.qq.com', 465)
server.login(sender_email, sender_password)

msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = receiver

server.sendmail(sender_email, [receiver], msg.as_string())
server.quit()
sent_count += 1
print(f" ✓ {receiver}")

except Exception as e:
print(f" ✗ {receiver}: {e}")
fail_count += 1

# 每批发送完后随机延时
if i + batch_size < total:
delay = random.uniform(*delay_range)
print(f"等待 {delay:.1f} 秒后发送下一批...")
time.sleep(delay)

print(f"发送完成:成功 {sent_count} 封,失败 {fail_count} 封")

本文仅供参考,不构成医疗建议。
本文由AI辅助创作,仅供参考。

滚动至顶部