exchangelib 是一个功能全面的库,它允许你通过 EWS (Exchange Web Services) 协议与 Microsoft Exchange Server 或 Office 365 (Microsoft 365) 进行交互。

核心概念
在开始之前,了解几个核心概念会很有帮助:
- EWS (Exchange Web Services): 这是 Microsoft 提供的一套 API,用于与 Exchange 服务器进行编程交互。
exchangelib就是对这套 API 的 Python 封装。 - Office 365 / Microsoft 365: 现代版的 Exchange 服务,通常通过 EWS 或 REST API 访问。
exchangelib完全支持它。 - 主账号 (Primary Account): 你用于登录 Exchange 服务器的邮箱地址。
- 代理账号 (Delegated Account): 如果你需要代表另一个用户(经理的邮箱)来操作邮件,就需要使用代理,这需要主账号有相应的权限。
第一步:安装 exchangelib
你需要安装这个库,打开你的终端或命令行,运行:
pip install exchangelib
第二步:连接到 Exchange 服务器
连接是所有操作的第一步,你需要提供一些关键信息:
- 邮箱地址: 你要操作的邮箱地址。
- 用户名: 你的登录用户名。
- 密码: 你的登录密码。注意:为了安全,强烈建议使用应用专用密码或 OAuth2 认证,而不是你的明文密码。
- 服务器: Exchange 服务器的 URL,对于 Office 365,通常是
outlook.office365.com。 - 版本: EWS 的版本。
exchangelib通常能自动检测,但手动指定可以避免问题。
连接示例代码
from exchangelib import Account, Configuration, Credentials, DELEGATE
# --- 1. 凭据 ---
# 将 'your_email@your_domain.com' 和 'your_password' 替换为你的实际信息
# 对于 Office 365,建议使用应用密码
credentials = Credentials(
username='your_email@your_domain.com',
password='your_password'
)
# --- 2. 配置 ---
# 对于 Office 365
config = Configuration(
server='outlook.office365.com',
credentials=credentials,
# auth_type='basic' # 可以显式指定,但 exchangelib 会自动处理
)
# --- 3. 账户 ---
# 'primary' 表示使用你自己的账号登录,可以访问自己的邮箱
# 'delegate' 表示代表另一个用户,需要相应权限
account = Account(
primary_smtp_address='your_email@your_domain.com',
config=config,
autodiscover=False, # 我们手动提供了 server,所以禁用自动发现
access_type=DELEGATE # 使用 DELEGATE 模式
)
print(f"成功连接到邮箱: {account.primary_smtp_address}")
关于认证的提示:

- 应用专用密码: 如果你开启了双重认证(MFA),必须使用应用专用密码,而不是你的登录密码。
- OAuth2: 这是更现代、更安全的认证方式。
exchangelib也支持,但配置稍复杂,需要注册应用并获取令牌。
第三步:常用邮箱操作
连接成功后,你就可以开始操作邮箱了。
查看邮件
exchangelib 提供了类似 Django ORM 的查询接口,非常方便。
# 获取收件箱中的邮件
inbox = account.inbox
print(f"收件箱名称: {inbox.name}")
# 获取最新的 10 封邮件(按接收时间降序)
all_emails = inbox.all().order_by('-datetime_received')[:10]
print("\n--- 最新 10 封邮件 ---")
for email in all_emails:
print(f"发件人: {email.sender.name} <{email.sender.email_address}>")
print(f"主题: {email.subject}")
print(f"时间: {email.datetime_received}")
print("-" * 20)
# 获取特定主题的邮件
emails_with_specific_subject = inbox.filter(subject="会议邀请")
for email in emails_with_specific_subject:
print(f"找到主题为 '会议邀请' 的邮件: {email.subject}")
发送邮件
发送邮件也很简单。
from exchangelib import Message
# 创建邮件对象
msg = Message(
account=account, # 使用已连接的账户
subject='来自 Python 的测试邮件',
body='这是一封通过 exchangelib 发送的测试邮件。',
to_recipients=['recipient_email@example.com'], # 收件人列表
cc_recipients=['cc_email@example.com'], # 抄送列表 (可选)
)
# 发送邮件
# send() 会立即发送,不会进入草稿箱
# save() 会保存到草稿箱
msg.send()
print("邮件已发送!")
移动邮件(移至“已删除”文件夹)
你可以将邮件从一个文件夹移动到另一个文件夹。
# 假设我们有一封特定的邮件
email_to_delete = inbox.all().first() # 获取第一封邮件
if email_to_delete:
# 获取“已删除邮件”文件夹
# 你可以通过文件夹名称或其内部 ID 来获取
trash_folder = account.trash
# 移动邮件
# move() 方法需要目标文件夹对象
email_to_move = email_to_move.move(trash_folder)
print(f"邮件 '{email_to_move.subject}' 已移至 '已删除邮件' 文件夹。")
创建和发送会议邀请
创建会议邀请与发送普通邮件类似,但使用的是 CalendarItem。
from exchangelib import CalendarItem
# 创建日历项(会议邀请)
meeting = CalendarItem(
account=account,
subject='团队周会',
body='讨论本周项目进展。',
start=datetime(2025, 10, 27, 14, 0, tzinfo=timezone.utc), # 开始时间,必须是时区感知的
end=datetime(2025, 10, 27, 15, 0, tzinfo=timezone.utc), # 结束时间
location='会议室 A',
to_recipients=['attendee1@example.com', 'attendee2@example.com']
)
# 发送会议邀请
# send() 会邀请所有收件人
meeting.send()
print("会议邀请已发送!")
注意: 时间对象必须是“时区感知”(timezone-aware) 的,建议使用 pytz 或 Python 3.9+ 内置的 zoneinfo 来处理时区。
搜索邮件
exchangelib 支持强大的搜索功能,包括全文搜索。
import pytz
# 在所有文件夹中搜索
# q 是 QuerySet 对象
search_results = account.all().filter(
subject__contains='重要',
datetime_received__range=(
datetime(2025, 1, 1, tzinfo=pytz.utc),
datetime(2025, 12, 31, tzinfo=pytz.utc)
)
)
print("\n--- 搜索结果 ---")
for item in search_results:
print(f"- {item.subject} ({item.datetime_received})")
# 在特定文件夹中搜索
inbox_search_results = account.inbox.filter(body__contains='项目报告')
完整示例:获取并处理邮件
下面是一个结合了连接、搜索和处理的完整示例。
from exchangelib import Account, Configuration, Credentials, DELEGATE, Message
from datetime import datetime, timezone
# --- 连接设置 ---
# 替换为你的信息
EMAIL = 'your_email@your_domain.com'
PASSWORD = 'your_app_password' # 强烈建议使用应用密码
SERVER = 'outlook.office365.com'
credentials = Credentials(username=EMAIL, password=PASSWORD)
config = Configuration(server=SERVER, credentials=credentials)
try:
account = Account(
primary_smtp_address=EMAIL,
config=config,
autodiscover=False,
access_type=DELEGATE
)
print(f"成功连接到 {account.primary_smtp_address}")
# --- 1. 获取并打印最新的 5 封邮件 ---
print("\n--- 最新 5 封邮件 ---")
latest_emails = account.inbox.all().order_by('-datetime_received')[:5]
for email in latest_emails:
print(f"主题: {email.subject}")
print(f"发件人: {email.sender.name}")
print(f"时间: {email.datetime_received.strftime('%Y-%m-%d %H:%M')}")
print("-" * 30)
# --- 2. 搜索特定关键词的邮件并回复 ---
keyword = "报告"
search_query = account.inbox.filter(subject__contains=keyword)
if search_query:
target_email = search_query.first()
if target_email:
print(f"\n找到一封关于 '{keyword}' 的邮件,主题为: '{target_email.subject}'")
# 创建回复邮件
reply = Message(
account=account,
subject=f"Re: {target_email.subject}",
body=f"您好,\n\n我已收到您关于 '{keyword}' 的邮件,\n\n谢谢!",
to_recipients=[target_email.sender],
references=target_email.message_id,
in_reply_to=target_email.message_id
)
reply.send()
print("回复邮件已发送!")
else:
print(f"\n在收件箱中没有找到包含 '{keyword}' 的邮件。")
except Exception as e:
print(f"发生错误: {e}")
总结与最佳实践
- 安全第一:永远不要在代码中硬编码密码,使用环境变量、配置文件或密钥管理服务来存储凭据。
- 错误处理:网络问题、权限不足、服务器错误等都可能发生,将你的代码包裹在
try...except块中,以优雅地处理异常。 - 使用应用专用密码:如果你有 MFA,这是必须的。
- 了解文件夹:Exchange 的文件夹结构(收件箱、已发送、草稿、垃圾邮件等)可以通过
account.root来浏览和访问。 - 性能考虑:
account.all()会获取文件夹中的所有项目,如果邮件数量巨大,可能会很慢,尽量使用filter()和order_by()来限制和优化查询结果。
exchangelib 的文档非常详尽,如果你需要更高级的功能(如处理任务、联系人、日历事件的更新/取消等),强烈建议查阅其官方文档。
