使用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.2imap_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 = partname_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_headerif isinstance(part, bytes): # 检查是否为字节串if charset: # 如果有字符集,按指定字符集解码decoded_part = part.decode(charset)else: # 没有指定字符集时尝试UTF-8解码,或选择其他策略decoded_part = part.decode('utf-8', errors='replace')else: # 如果已经是字符串,直接使用decoded_part = partreturn decoded_partelse:# 如果解码结果不是元组,直接返回解码后的字符串return None
正文获取部分
def extract_text_body(email_message):"""从电子邮件消息中提取纯文本正文。参数:email_message: 一个电子邮件消息对象,可以是使用Python email库构建的或从文件中读取的。返回:一个字符串,包含电子邮件的纯文本正文。如果没有找到纯文本正文或邮件为空,则返回空字符串。"""# 初始化一个字符串,用于存储提取的文本正文all_text_body = ""# 遍历电子邮件的每一个部分for part in email_message.walk():# 获取当前部分的Content-Typectype = part.get_content_type()# 获取当前部分的Content-Dispositioncdispo = 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 = 3for 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