使用IMAP服务获取163邮箱的未读邮件
整体的逻辑思路如下:
- 开启163邮箱的IMAP服务,拿到授权码用于登录IMAP服务
- 登录IMAP服务,获取邮箱的未读邮件列表
- 遍历未读邮件列表,获取邮件内容
# 导入必要的库
import os
import imaplib
import ssl
import email
from email.header import decode_header
from email.utils import parseaddr
1. 开启163邮箱的IMAP服务,拿到授权码用于登录IMAP服务
163邮箱设置IMAP服务,需要到邮箱设置页面,选择“邮箱设置”->“POP3/IMAP”->“开启IMAP服务”,然后点击“保存”,即可开启IMAP服务。
2. 登录IMAP服务,获取邮箱的未读邮件列表
# 创建一个默认的SSL上下文对象,用于服务器认证
# 参数设置为None,表示使用默认值,后续将通过代码明确指定SSL/TLS版本范围
ssl_context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)
# 指定SSL/TLS的最小版本为TLS 1.2,以确保连接使用的协议不低于此版本
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
# 指定SSL/TLS的最大版本为TLS 1.3,以确保连接不会使用超出此版本的协议
# TLS 1.3是最新的TLS版本,提供了更强的安全性和加密方法
ssl_context.maximum_version = ssl.TLSVersion.TLSv1_3
def login163(user=None, pwd=None, host="imap.163.com" ):
# 尝试建立SSL加密的IMAP连接并登录
# 异常处理增强
try:
imap_client = imaplib.IMAP4_SSL(host, ssl_context=ssl_context) # 确保使用TLS 1.2
imap_client.login(user, pwd)
except imaplib.IMAP4.error as e:
print(f"登录失败: {e}")
return
# 发送ID命令给服务器,提供客户端信息
imaplib.Commands["ID"] = ('AUTH',)
args = ("name", user, "contact", user, "version", "1.0.0", "vendor", "myclient")
imap_client._simple_command("ID", str(args).replace(",", "").replace("'", "\""))
return imap_client
3. 遍历未读邮件列表,获取邮件内容
def fetch_and_mark_emails_as_read(imap_client,mailbox="INBOX"):
"""
从指定的邮箱中获取未读邮件,并将这些邮件标记为已读。
:param imap_client: IMAP客户端对象,用于与邮件服务器交互
:param mailbox: string, 指定的邮箱,默认为"INBOX"(收件箱)
"""
# 选择邮箱,默认为收件箱
imap_client.select(mailbox)
# 搜索未读邮件
typ, dat = imap_client.search(None, "UNSEEN")
# 解码邮件编号字符串
str_numbers = dat[0].decode('utf-8')
# 将编号字符串分割成列表
numbers_str_list = str_numbers.split()
# 遍历未读邮件
for msg in numbers_str_list:
try:
# 获取邮件内容
_, data = imap_client.fetch(msg, '(RFC822)')
raw_email = data[0][1]
email_message = email.message_from_bytes(raw_email)
# 解码邮件主题
subject = decode_email_header(email_message['Subject'])
# 解码发件人信息
from_info = decode_header(email_message['From'])
name_parts = []
for part, charset in from_info:
if isinstance(part, bytes):
if charset:
decoded_part = part.decode(charset)
else:
decoded_part = part.decode('utf-8', errors='replace')
else:
decoded_part = part
name_parts.append(decoded_part)
full_name = ''.join(name_parts).strip()
from_person,_=parseaddr(full_name)
# 提取邮件正文
all_text_body = extract_text_body(email_message)
#------------------------------
#你对得到的邮件的处理代码
#------------------------------
# 标记邮件为已读
imap_client.store(msg, '+FLAGS', '\\Seen')
except imaplib.IMAP4.error as e:
print(f"处理邮件或标记已读失败: {e}")
邮件主题解码函数:
def decode_email_header(header):
"""
解码电子邮件头信息。
电子邮件头信息可能包含多种编码,这个函数旨在解析头信息并返回解码后的字符串。
如果头信息是ASCII码,则直接返回;如果是非ASCII码,会根据编码类型进行解码。
参数:
header (str): 需要解码的电子邮件头信息。
返回:
str: 解码后的字符串。如果解码过程中包含多种编码,会返回一个元组,包含解码后的字符串和对应的编码类型。
"""
# 解码头信息,返回一个包含解码结果和编码类型的元组
# 列表
decoded_header = decode_header(header)[0]
# print(decoded_header)
# 判断解码结果是否为元组,如果是,说明存在多种编码,需要进一步解码
if isinstance(decoded_header, tuple):
# 对元组中的字符串进行解码,并返回解码后的结果
part, charset = decoded_header
if isinstance(part, bytes): # 检查是否为字节串
if charset: # 如果有字符集,按指定字符集解码
decoded_part = part.decode(charset)
else: # 没有指定字符集时尝试UTF-8解码,或选择其他策略
decoded_part = part.decode('utf-8', errors='replace')
else: # 如果已经是字符串,直接使用
decoded_part = part
return decoded_part
else:
# 如果解码结果不是元组,直接返回解码后的字符串
return None
正文获取部分
def extract_text_body(email_message):
"""
从电子邮件消息中提取纯文本正文。
参数:
email_message: 一个电子邮件消息对象,可以是使用Python email库构建的或从文件中读取的。
返回:
一个字符串,包含电子邮件的纯文本正文。如果没有找到纯文本正文或邮件为空,则返回空字符串。
"""
# 初始化一个字符串,用于存储提取的文本正文
all_text_body = ""
# 遍历电子邮件的每一个部分
for part in email_message.walk():
# 获取当前部分的Content-Type
ctype = part.get_content_type()
# 获取当前部分的Content-Disposition
cdispo = str(part.get('Content-Disposition'))
# 检查当前部分是否为纯文本且不是附件
if ctype == 'text/plain' and 'attachment' not in cdispo:
# 获取当前部分的payload(实际内容),并解码
body = part.get_payload(decode=True)
# 获取当前部分的内容字符集
charset = part.get_content_charset()
# 将解码后的文本添加到存储所有文本正文的字符串中
all_text_body += body.decode(charset, errors='replace')
# 找到纯文本正文后立即终止循环,以提高效率
break # 如果找到文本部分,即停止搜索,提高效率
# 返回收集到的所有文本正文
return all_text_body
测试代码
def imap_mail_get(client):
retry_limit = 3
for i in range(retry_limit):
try:
if client is None:
client = login163(username, password)#password为IMAP授权码
# 对两个邮箱进行操作
for mailbox in ["INBOX", "Sent"]:
fetch_and_mark_emails_as_read(client, mailbox=mailbox)
return client
except Exception as e:
pass