1. 项目缘起为什么需要自动化处理QQ邮箱在日常工作中我们经常会遇到一些重复性的邮件处理任务。比如我最近就接手了一个小项目需要定期从某个服务拉取数据生成报告然后通过邮件自动发送给团队成员。手动操作不仅耗时还容易忘记。另一个常见的场景是我们需要监控一个公共邮箱自动收取特定主题或发件人的邮件提取附件或内容进行后续处理。这时候用Python脚本来实现邮件的自动收发就成了一个非常自然的选择。QQ邮箱作为国内用户基数最大的免费邮箱服务之一其稳定性和易用性有目共睹。虽然它提供了网页端和客户端但对于自动化需求我们更需要的是程序化的接口。Python的smtplib和imaplib或poplib库正是连接程序与QQ邮箱服务器的桥梁。前者负责“发”后者负责“收”。网上虽然有很多零散的代码片段但要么过于简略缺少关键细节要么环境配置部分一笔带过让新手尤其是对网络协议不熟悉的朋友在第一步“获取授权码”上就卡住了。所以这篇内容的目标就是充当你的“操作手册”。我会从一个完整的、可运行的脚本出发拆解每一个步骤解释背后的协议原理用最通俗的话并重点分享那些官方文档不会写、但实际调试中一定会遇到的“坑”。无论你是想实现一个简单的生日祝福邮件自动发送器还是构建一个复杂的邮件监控与处理流水线这里的内容都能给你一个扎实的起点。2. 核心原理与前置准备理解协议与获取“钥匙”在开始写代码之前我们必须搞清楚两件事邮件是怎么发出去的又是怎么收过来的这涉及到两个核心协议SMTP和IMAP/POP3。你可以把它们想象成邮局系统。SMTPSimple Mail Transfer Protocol简单邮件传输协议。它的职责就像邮局的“寄件窗口”。你的Python程序扮演寄件人连接到QQ邮箱的SMTP服务器地址是smtp.qq.com端口通常是465或587验证身份后把写好的邮件“包裹”交给它它负责投递到收件人的邮箱服务器。这里的关键是“验证身份”你不能随便冒充一个人发邮件所以需要密码。但注意QQ邮箱这里需要的不是你的登录密码而是一个专用的“授权码”。IMAPInternet Message Access Protocol和POP3Post Office Protocol - Version 3这两个协议都是负责“收件”的但工作方式不同。IMAP更像在云端操作邮件你在客户端比如手机邮件App上的操作读信、移动、删除会同步到服务器而POP3则是把邮件从服务器下载到本地之后的操作与服务器无关。对于自动化处理我强烈推荐使用IMAP因为它允许我们在服务器上直接搜索、筛选邮件而不必全部下载更灵活高效。QQ邮箱的IMAP服务器地址是imap.qq.com端口是993。现在我们来获取那把最重要的“钥匙”——QQ邮箱的授权码。这是整个流程中最容易出错的一步。登录网页版QQ邮箱用你的QQ账号密码正常登录。进入设置在邮箱首页点击顶部的“设置”齿轮图标。选择“账户”选项卡在设置页面中找到并点击“账户”。开启服务向下滚动找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务”这一栏。开启IMAP/SMTP服务你会看到“IMAP/SMTP服务”和“POP3/SMTP服务”的选项。点击“IMAP/SMTP服务”后的“开启”。根据指引可能需要验证密保。获取授权码服务开启后页面会提示你“生成授权码”。点击生成它会要求你通过短信验证。验证成功后你会得到一个16位的字符串由字母和数字组成。请立即妥善保存这个字符串它只会显示一次这就是你的程序登录邮箱的密码。注意这个授权码是独立于你QQ登录密码的。即使你的QQ密码改了这个授权码依然有效除非你在邮箱设置里关闭服务并重新生成。使用授权码而不是直接使用QQ密码是更安全的方式。环境准备确保你的Python环境是3.6及以上版本。不需要安装额外的库因为smtplib和imaplib都是Python的标准库内置的。你可以通过命令行输入python --version来确认。3. 实战第一步用Python发送QQ邮件发送邮件主要使用smtplib库。我们的目标是构建一个健壮的发送函数能够处理文本邮件、HTML邮件以及带附件的邮件。下面我将分步拆解并解释每个参数的意义。3.1 构建基础发送函数首先我们创建一个最基本的发送纯文本邮件的函数。这里会用到email.mime模块中的MIMEText来构建邮件内容。import smtplib from email.mime.text import MIMEText from email.header import Header def send_qq_email_text(sender, auth_code, receivers, subject, content): 发送纯文本邮件 :param sender: 发件人QQ邮箱如 123456789qq.com :param auth_code: QQ邮箱SMTP授权码 :param receivers: 收件人列表如 [friend1xx.com, friend2xx.com] :param subject: 邮件主题 :param content: 邮件正文内容纯文本 # 1. 构造MIMEText对象指定纯文本格式 # ‘plain’ 表示纯文本 ‘utf-8’ 确保中文兼容 message MIMEText(content, plain, utf-8) # 2. 设置邮件头部信息 message[From] Header(f我的Python程序 {sender}, utf-8) # 发件人显示名称 message[To] Header(,.join(receivers), utf-8) # 收件人 message[Subject] Header(subject, utf-8) # 主题 try: # 3. 连接QQ邮箱SMTP服务器端口465使用SSL加密 smtp_obj smtplib.SMTP_SSL(smtp.qq.com, 465) # 4. 设置调试级别生产环境可设为0 smtp_obj.set_debuglevel(1) # 5. 登录邮箱注意这里密码是授权码 smtp_obj.login(sender, auth_code) # 6. 发送邮件 smtp_obj.sendmail(sender, receivers, message.as_string()) print(邮件发送成功) except smtplib.SMTPException as e: print(f邮件发送失败: {e}) finally: # 7. 关闭连接 if smtp_obj in locals(): smtp_obj.quit() # 使用示例 if __name__ __main__: my_sender 你的QQ号qq.com # 替换为你的QQ邮箱 my_auth_code 你的16位授权码 # 替换为你的授权码 my_receivers [收件人1xx.com] # 可以是任意邮箱 send_qq_email_text(my_sender, my_auth_code, my_receivers, Python测试邮件, 这是一封来自Python程序的测试邮件)关键点解析SMTP_SSL因为QQ邮箱的465端口要求SSL加密连接所以我们使用SMTP_SSL类。如果你的环境不支持SSL极少数情况可以尝试使用SMTP连接587端口并调用starttls()方法加密但465端口是更通用和推荐的方式。login第一个参数是发件邮箱第二个参数是授权码不是QQ密码。message.as_string()将构建好的MIME对象转换为符合SMTP协议要求的字符串格式。set_debuglevel(1)这在调试时非常有用会在控制台打印出客户端与服务器之间的所有通信内容帮你定位问题。正式运行时可以关闭设为0。3.2 升级发送HTML格式邮件与带附件的邮件纯文本太单调了我们经常需要发送带格式的邮件或者报告附件。这就需要用到MIMEMultipart来组合多种类型的内容。from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication import os def send_qq_email_with_attachment(sender, auth_code, receivers, subject, text_contentNone, html_contentNone, attachment_pathsNone): 发送支持纯文本、HTML和附件的邮件 :param text_content: 纯文本正文可选 :param html_content: HTML格式正文可选 :param attachment_paths: 附件文件路径列表可选 # 创建一个混合类型的MIME对象作为邮件的根容器 message MIMEMultipart(mixed) message[From] Header(f报告发送器 {sender}, utf-8) message[To] Header(,.join(receivers), utf-8) message[Subject] Header(subject, utf-8) # 如果同时提供了文本和HTML创建‘alternative’部分让邮件客户端择优显示 if text_content and html_content: alt_part MIMEMultipart(alternative) message.attach(alt_part) alt_part.attach(MIMEText(text_content, plain, utf-8)) alt_part.attach(MIMEText(html_content, html, utf-8)) elif text_content: message.attach(MIMEText(text_content, plain, utf-8)) elif html_content: message.attach(MIMEText(html_content, html, utf-8)) # 添加附件 if attachment_paths: for file_path in attachment_paths: if not os.path.isfile(file_path): print(f警告附件文件 {file_path} 不存在已跳过。) continue # 读取文件二进制内容 with open(file_path, rb) as f: file_data f.read() # 获取文件名 file_name os.path.basename(file_path) # 创建MIMEApplication对象表示附件 att MIMEApplication(file_data) # 设置附件头信息filename指定下载时的文件名 att.add_header(Content-Disposition, attachment, filenameHeader(file_name, utf-8).encode()) # 将附件添加到根容器 message.attach(att) print(f已添加附件: {file_name}) # 发送部分与之前相同 try: smtp_obj smtplib.SMTP_SSL(smtp.qq.com, 465) smtp_obj.login(sender, auth_code) smtp_obj.sendmail(sender, receivers, message.as_string()) print(复杂邮件发送成功) except Exception as e: print(f发送失败: {e}) finally: if smtp_obj in locals(): smtp_obj.quit() # 使用示例 if __name__ __main__: my_sender 你的QQ号qq.com my_auth_code 你的授权码 my_receivers [收件人xx.com] html_content html body h1 stylecolor: #5e9ca0;每日数据报告/h1 p您好这是自动生成的报告/p ul li新增用户 strong152/strong 人/li li订单总数 strong89/strong 笔/li /ul p详细数据请查看附件。/p /body /html send_qq_email_with_attachment( sendermy_sender, auth_codemy_auth_code, receiversmy_receivers, subject【系统自动】每日业务报告, text_content您好今日报告已生成详情请查看HTML内容或附件。, # 为不支持HTML的客户端提供备选 html_contenthtml_content, attachment_paths[./report_20231027.pdf, ./data.csv] # 替换为实际文件路径 )经验之谈mixedvsalternativeMIMEMultipart(mixed)是用于混合独立部分如正文和附件。而MIMEMultipart(alternative)用于表示同一内容的不同版本如纯文本和HTML邮件客户端会选择它能显示的最佳版本。我们将alternative部分作为一个整体挂载到mixed的根下面。附件文件名编码如果附件名包含中文必须使用Header进行编码否则在某些邮箱客户端会显示乱码。错误处理附件路径检查很重要。如果文件不存在脚本应该跳过并继续而不是崩溃。4. 实战第二步用Python收取与解析QQ邮件收取邮件我们使用imaplib库。相比POP3IMAP允许我们在服务器上执行搜索、标记等操作非常适合自动化处理。我们的目标是连接到邮箱搜索特定条件的邮件获取其内容并解析。4.1 连接邮箱与搜索邮件首先我们实现一个函数来连接IMAP服务器并获取邮件ID列表。import imaplib import email from email.header import decode_header import re def connect_and_search_imap(mail_user, auth_code, search_criteriaALL, mailboxINBOX): 连接IMAP服务器并搜索邮件 :param mail_user: QQ邮箱地址 :param auth_code: QQ邮箱授权码 :param search_criteria: IMAP搜索条件默认为ALL所有邮件 :param mailbox: 要操作的邮箱文件夹默认为INBOX收件箱 :return: (imap连接对象, 邮件ID列表) try: # 1. 连接IMAP服务器使用SSL imap_server imap.qq.com imap_port 993 imap_conn imaplib.IMAP4_SSL(imap_server, imap_port) print(f已连接到 {imap_server}) # 2. 登录 imap_conn.login(mail_user, auth_code) print(登录成功) # 3. 选择邮箱文件夹 status, _ imap_conn.select(mailbox, readonlyTrue) # readonlyTrue避免误操作 if status ! OK: raise Exception(f无法选择邮箱文件夹: {mailbox}) print(f已选择文件夹: {mailbox}) # 4. 搜索邮件 status, message_ids imap_conn.search(None, search_criteria) if status ! OK: raise Exception(邮件搜索失败) # message_ids 返回的是字节字符串如 b1 2 3 10 id_list message_ids[0].split() print(f找到 {len(id_list)} 封符合条件的邮件) return imap_conn, id_list except imaplib.IMAP4.error as e: print(fIMAP连接或登录错误: {e}) return None, [] except Exception as e: print(f其他错误: {e}) return None, [] # 使用示例搜索最近一天内主题包含“报告”的未读邮件 if __name__ __main__: my_user 你的QQ号qq.com my_auth 你的授权码 # 构建搜索条件 # ‘UNSEEN’ 表示未读邮件 # ‘SINCE’ 表示从某个日期之后日期格式为 “DD-MMM-YYYY”例如 “27-Oct-2023” # ‘SUBJECT’ 表示主题包含某关键词 search_query (UNSEEN SINCE 27-Oct-2023 SUBJECT 报告) conn, ids connect_and_search_imap(my_user, my_auth, search_criteriasearch_query) if conn and ids: # 后续处理邮件... pass if conn: conn.logout()IMAP搜索条件语法这是IMAP协议的核心能力之一。条件用括号括起可以用AND、OR、NOT组合。ALL所有邮件。UNSEEN未读邮件。FROM “someonedomain.com”来自特定发件人。SUBJECT “关键词”主题包含关键词。SINCE “01-Jan-2023”指定日期之后包含该日。BEFORE “01-Jan-2023”指定日期之前不包含该日。(UNSEEN SINCE “01-Jan-2023”)组合条件2023年1月1日以来的未读邮件。4.2 解析邮件内容与附件获取邮件ID后我们需要根据ID获取邮件的完整原始数据RFC 822格式然后使用email库进行解析。def parse_email_message(raw_email): 解析原始的邮件数据 :param raw_email: 邮件的原始字节数据 :return: 字典包含解析后的主题、发件人、日期、正文、附件信息等 # 将字节数据转换为email.message.Message对象 msg email.message_from_bytes(raw_email) result { subject: , from: , date: , text_body: , html_body: , attachments: [] # 每个元素为 (filename, file_data) } # 解码邮件头主题、发件人、日期 def decode_str(s): if s is None: return value, charset decode_header(s)[0] if charset: return value.decode(charset) else: # 如果charset是None可能value已经是字符串或bytes return value if isinstance(value, str) else value.decode(utf-8, errorsignore) result[subject] decode_str(msg.get(Subject)) result[from] decode_str(msg.get(From)) result[date] decode_str(msg.get(Date)) # 递归遍历邮件的各个部分可能是多部分嵌套 def parse_part(part, text_body, html_body, attachments): content_type part.get_content_type() content_disposition str(part.get(Content-Disposition)) # 如果是附件有Content-Disposition且为‘attachment’ if attachment in content_disposition: filename part.get_filename() if filename: filename decode_str(filename) file_data part.get_payload(decodeTrue) # 解码附件内容 attachments.append((filename, file_data)) print(f 发现附件: {filename}) # 如果是文本正文 elif content_type text/plain: # 获取正文内容并处理编码 payload part.get_payload(decodeTrue) charset part.get_content_charset() or utf-8 try: text_body.append(payload.decode(charset, errorsreplace)) except (LookupError, TypeError): text_body.append(payload.decode(utf-8, errorsreplace)) # 如果是HTML正文 elif content_type text/html: payload part.get_payload(decodeTrue) charset part.get_content_charset() or utf-8 try: html_body.append(payload.decode(charset, errorsreplace)) except (LookupError, TypeError): html_body.append(payload.decode(utf-8, errorsreplace)) # 如果部分本身又是一个多部分容器如 multipart/alternative elif content_type.startswith(multipart/): for subpart in part.get_payload(): parse_part(subpart, text_body, html_body, attachments) text_body_parts [] html_body_parts [] attachments [] # 开始解析 parse_part(msg, text_body_parts, html_body_parts, attachments) result[text_body] \n.join(text_body_parts) result[html_body] \n.join(html_body_parts) result[attachments] attachments return result def fetch_and_process_emails(imap_conn, message_id_list, limit5): 获取并处理指定ID列表的邮件 :param imap_conn: 已连接的IMAP对象 :param message_id_list: 邮件ID列表 :param limit: 最多处理多少封防止一次处理过多 processed_emails [] for i, msg_id in enumerate(message_id_list[-limit:]): # 处理最新的几封 print(f\n处理邮件 ID: {msg_id.decode()}) try: # 使用‘RFC822’获取邮件完整原始数据 status, data imap_conn.fetch(msg_id, (RFC822)) if status ! OK: print(f 获取邮件 {msg_id} 失败) continue # data的结构是 [(b‘...‘, b‘原始邮件数据...‘)], 取第二个元素 raw_email data[0][1] email_info parse_email_message(raw_email) processed_emails.append(email_info) # 打印摘要信息 print(f 主题: {email_info[subject][:50]}...) print(f 发件人: {email_info[from]}) print(f 日期: {email_info[date]}) print(f 文本正文长度: {len(email_info[text_body])}) print(f HTML正文长度: {len(email_info[html_body])}) print(f 附件数量: {len(email_info[attachments])}) # 示例将附件保存到本地 for filename, filedata in email_info[attachments]: safe_filename re.sub(r[\\/*?:|], _, filename) # 清理非法文件名 with open(f./downloads/{safe_filename}, wb) as f: f.write(filedata) print(f 附件已保存: {safe_filename}) except Exception as e: print(f 处理邮件 {msg_id} 时出错: {e}) continue return processed_emails # 整合使用 if __name__ __main__: my_user 你的QQ号qq.com my_auth 你的授权码 conn, ids connect_and_search_imap(my_user, my_auth, search_criteriaUNSEEN) if conn and ids: emails fetch_and_process_emails(conn, ids, limit3) print(f\n共处理了 {len(emails)} 封新邮件。) # 处理完后可以将这些邮件标记为已读非只读模式下 # for msg_id in ids[-3:]: # conn.store(msg_id, FLAGS, \\Seen) conn.logout()解析过程中的关键细节与避坑指南编码问题邮件头主题、发件人名称和附件文件名可能使用多种编码如GBK、UTF-8、Base64。decode_header函数是处理这个问题的瑞士军刀它能返回解码后的字符串和使用的字符集。务必使用它来处理所有头部信息。递归解析一封邮件可能结构非常复杂比如嵌套的multipart/mixed里面包含multipart/alternative文本和HTML再包含附件。我们的parse_part函数通过递归调用自身来处理这种嵌套结构确保不漏掉任何部分。正文解码获取正文部分text/plain或text/html的payload时需要使用get_payload(decodeTrue)来解码Content-Transfer-Encoding如base64, quoted-printable。然后再使用part.get_content_charset()获取字符集进行解码。如果字符集获取失败则默认使用utf-8并用errorsreplace容忍解码错误。附件保存附件数据是二进制格式直接写入文件即可。注意文件名可能包含路径分隔符等非法字符最好进行清洗如示例中使用正则表达式替换。5. 进阶技巧与生产环境考量将上述基础功能组合起来你已经可以完成大多数自动化任务了。但在实际生产环境中我们还需要考虑更多。5.1 构建一个健壮的邮件处理类将连接、发送、接收、解析功能封装到一个类中管理连接状态更易于使用和维护。class QQMailHandler: def __init__(self, email_address, auth_code): self.email email_address self.auth_code auth_code self.smtp_conn None self.imap_conn None def send_mail(self, receivers, subject, content, is_htmlFalse, attachmentsNone): 发送邮件简化版可基于前面函数扩展 # ... 整合前面的发送逻辑 ... pass def connect_imap(self): 建立IMAP连接 if self.imap_conn: try: self.imap_conn.noop() # 发送NOOP命令检查连接是否存活 except: self.imap_conn None if not self.imap_conn: self.imap_conn imaplib.IMAP4_SSL(imap.qq.com, 993) self.imap_conn.login(self.email, self.auth_code) return self.imap_conn def fetch_unread_by_subject(self, keyword): 获取主题包含关键字的未读邮件 conn self.connect_imap() conn.select(INBOX) # 使用CHARSET UTF-8确保中文关键词搜索正常 status, ids conn.search(UTF-8, f(UNSEEN SUBJECT {keyword})) if status OK and ids[0]: return self.fetch_and_parse(conn, ids[0].split()) return [] def fetch_and_parse(self, conn, id_list): 获取并解析邮件整合前面的逻辑 # ... 整合前面的获取与解析逻辑 ... pass def mark_as_read(self, msg_id): 将邮件标记为已读需在select时设置readonlyFalse if self.imap_conn: self.imap_conn.store(msg_id, FLAGS, \\Seen) def close(self): 关闭连接 if self.smtp_conn: self.smtp_conn.quit() if self.imap_conn: self.imap_conn.logout() # 使用示例 handler QQMailHandler(your_emailqq.com, your_auth_code) unread_reports handler.fetch_unread_by_subject(日报) for mail in unread_reports: print(mail[subject]) # 处理邮件内容... # handler.mark_as_read(mail_id) # 可选标记为已读 handler.close()5.2 错误处理、重试与日志记录网络操作不稳定必须加入重试机制和详细的日志。import logging import time logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) def send_mail_with_retry(mail_handler, receivers, subject, content, max_retries3): 带重试的邮件发送 for attempt in range(max_retries): try: mail_handler.send_mail(receivers, subject, content) logger.info(f邮件发送成功。主题{subject}) return True except (smtplib.SMTPException, ConnectionError) as e: logger.warning(f第{attempt1}次发送失败: {e}) if attempt max_retries - 1: wait_time 2 ** attempt # 指数退避 logger.info(f{wait_time}秒后重试...) time.sleep(wait_time) else: logger.error(f邮件发送最终失败主题{subject}) return False5.3 安全与配置管理绝对不要将邮箱地址和授权码硬编码在脚本中尤其是当你打算将代码上传到GitHub等公共平台时。使用环境变量# 在终端中设置临时 export QQ_MAIL_USERyour_emailqq.com export QQ_MAIL_AUTH_CODEyour_auth_code# 在Python中读取 import os mail_user os.environ.get(QQ_MAIL_USER) auth_code os.environ.get(QQ_MAIL_AUTH_CODE) if not mail_user or not auth_code: raise ValueError(请设置QQ_MAIL_USER和QQ_MAIL_AUTH_CODE环境变量)使用配置文件创建一个config.ini或config.yaml文件将其加入.gitignore。# config.ini [qqmail] user your_emailqq.com auth_code your_auth_codeimport configparser config configparser.ConfigParser() config.read(config.ini) mail_user config[qqmail][user] auth_code config[qqmail][auth_code]5.4 性能优化处理大量邮件当需要处理成千上万封邮件时逐封fetch效率很低。批量获取imaplib的fetch命令支持一次获取多个邮件ID的数据但需要注意服务器可能对单次响应大小有限制。可以尝试imap_conn.fetch(1:10, (RFC822))来获取ID 1到10的邮件具体语法取决于服务器实现QQ邮箱支持序列集表示。分页处理不要一次性搜索所有邮件。结合SINCE和BEFORE进行分时段处理或者每次处理一定数量后将已处理的邮件移动到其他文件夹使用imap_conn.copy和imap_conn.store(..., FLAGS, \\Deleted)后expunge减少下次搜索的范围。选择性获取如果只需要邮件头主题、发件人、日期而不需要正文和附件可以使用(RFC822.HEADER)代替(RFC822)能极大减少网络传输数据量。6. 常见问题排查与解决方案即使按照步骤操作你也可能会遇到一些问题。这里列出一些我踩过的坑和解决方案。问题1smtplib.SMTPAuthenticationError: (535, b‘Error: authentication failed‘)原因99%密码错误。请再次确认你使用的是16位的授权码而不是QQ登录密码。去邮箱设置里重新生成一次并确保复制完整。其他可能账号未开启SMTP服务。请回到邮箱设置的“账户”页确认“IMAP/SMTP服务”是“已开启”状态。问题2ssl.SSLError: [SSL: WRONG_VERSION_NUMBER]或连接超时原因端口或加密方式错误。QQ邮箱的SMTP SSL端口是465IMAP SSL端口是993。确保你没有错误地使用了25、587STARTTLS或143端口。公司网络有时会屏蔽这些端口请确认网络策略。问题3发送邮件成功但收件人收不到或被放入垃圾箱原因邮件内容被反垃圾邮件策略拦截。解决方案规范发件人message[‘From‘]地址必须和登录的邮箱一致。避免敏感词主题和正文避免使用大量促销、金融类敏感词汇。添加纯文本版本即使发送HTML邮件也最好附带一个text/plain版本如前面示例所示。控制频率短时间内发送大量邮件容易被判定为垃圾邮件。合理设置发送间隔。问题4imaplib.error: command SEARCH illegal in state AUTH, only allowed in states SELECTED原因在执行search命令前没有用select()方法选择一个邮箱文件夹如‘INBOX‘。必须在select之后才能进行搜索、获取等操作。问题5中文主题或附件名显示为乱码原因没有正确解码Header。解决方案务必使用email.header.decode_header()函数来处理所有从邮件头获取的字符串如示例代码所示。对于附件文件名在添加到Content-Disposition头时也需要用Header进行编码。问题6脚本在后台运行一段时间后IMAP操作超时或断开原因IMAP服务器有连接空闲超时机制。解决方案实现连接保活。可以在长时间空闲后先捕获可能发生的超时异常然后重新建立连接。或者定期例如每10分钟发送一个imap_conn.noop()命令这是一个空操作用于告诉服务器连接仍活跃。将这些代码片段和理解结合起来你已经拥有了一个强大的QQ邮箱自动化工具箱。从简单的通知发送到复杂的邮件内容监控与提取都可以在此基础上构建。记住关键永远是细节授权码、编码、协议状态和异常处理。