IMAPLIB无法识别XML格式.xls后缀邮件附件的问题求解
用户问题
使用imaplib处理邮件时所有功能运行正常,仅当附件为XML格式电子表格且后缀为.XLS时,现有代码无法识别该类附件,相关代码如下:
for self.i in tqdm(range(latest_email_id, oldest_email_id - 1, -1)): print("Incrementation:",self.i) #x = mail[i-1] status = "" xdeleted = False typ, x = self.m.fetch(str(self.i), '(RFC822)') raw_email = x[0][1] raw_email_string = raw_email.decode() email_message = email.message_from_string(raw_email_string) for response_part in x: if isinstance(response_part, tuple): msg = email.message_from_string(response_part[1].decode()) #print(x.senton.date()) #sentondate = pd.to_datetime(x.senton.date()) senderemail = msg['from'] subject = msg['subject'].replace("\n","").replace("\r","") datemsg = msg["Date"] subject, encoding = email.header.decode_header(subject)[0] if (encoding == None): subject = subject else: subject = subject.decode(encoding) print("subject:", subject) print("Date:", datemsg) attachmentsfilenamesconcat = "" attachments = [] for part in email_message.walk(): if part.get_content_maintype() == 'multipart': continue if part.get('Content-Disposition') is None: continue fileName, encoding = decode_header(str(part.get_filename()))[0] if (encoding is None): fileName = fileName.replace("\r","").replace("\n","") else: fileName = fileName.decode(encoding).replace("\r","").replace("\n","") attachments.append(fileName) if bool(fileName): fnwoext, file_extension = os.path.splitext(os.path.join(self.DIR_ATT, fileName)) if ((file_extension.lower() == ".csv") | (file_extension.lower() == ".xls") | ( file_extension.lower() == ".xlsx") \ | (file_extension.lower() == ".xlsm") | (file_extension.lower() == ".pdf") | ( file_extension.lower() == ".zip") \ | (file_extension.lower() == ".txt")): print(os.path.join(self.DIR_ATT, fileName)) filePath = os.path.join(self.DIR_ATT,fileName) fp = open(filePath, 'wb') fp.write(part.get_payload(decode = True)) fp.close()
请问需要调整哪部分语法逻辑,才能正常检测到这类附件?
问题原因
你遇到的识别失败是因为XML格式的.XLS文件(即XML Spreadsheet 2003格式)通常存在邮件头异常问题,现有逻辑有两处过滤规则会漏掉这类附件:
- 部分邮件客户端发送这类文件时,不会在
Content-Disposition头中标记为attachment,会被你代码中if part.get('Content-Disposition') is None: continue直接过滤 - 部分场景下这类文件的文件名存储在
Content-Type的name参数中,part.get_filename()在部分Python版本中无法读取该参数,导致拿到空文件名被过滤 - 代码存在冗余的两次邮件对象解析逻辑,解析头部用的
msg和遍历附件用的email_message是独立的两个对象,极端场景下会出现内容不一致
修改方案
需要调整两处核心逻辑:
1. 删掉冗余的邮件解析代码,统一使用同一个邮件对象
把原来重复解析msg的逻辑删掉,所有头部信息直接从email_message中读取:
typ, x = self.m.fetch(str(self.i), '(RFC822)') raw_email = x[0][1] raw_email_string = raw_email.decode() email_message = email.message_from_string(raw_email_string) # 删掉下面冗余的解析逻辑 # for response_part in x: # if isinstance(response_part, tuple): # msg = email.message_from_string(response_part[1].decode()) # 后面头部读取统一用email_message senderemail = email_message['from'] subject = email_message['subject'].replace("\n","").replace("\r","") datemsg = email_message["Date"]
2. 调整附件读取逻辑,兼容异常头信息的附件
把附件遍历部分的代码调整为如下内容:
for part in email_message.walk(): if part.get_content_maintype() == 'multipart': continue # 优先读Content-Disposition的filename,读不到则尝试读Content-Type的name参数 fileName = part.get_filename() if not fileName: content_type = part.get('Content-Type', '') if 'name=' in content_type: fileName = content_type.split('name=')[1].strip('"\' ') # 无明确文件名的才跳过,不再以Content-Disposition是否存在作为过滤条件 if not fileName: continue # 解码文件名,不要提前转str避免把None转为字符串"None" fileName, encoding = decode_header(fileName)[0] if encoding: fileName = fileName.decode(encoding) fileName = fileName.replace("\r","").replace("\n","").strip() attachments.append(fileName) fnwoext, file_extension = os.path.splitext(fileName) # 原后缀判断逻辑保留即可,转小写后.XLS会自动匹配到.xls规则 if file_extension.lower() in [".csv", ".xls", ".xlsx", ".xlsm", ".pdf", ".zip", ".txt"]: filePath = os.path.join(self.DIR_ATT, fileName) with open(filePath, 'wb') as fp: fp.write(part.get_payload(decode = True))
调整完成后即可正常识别这类后缀为.XLS的XML格式电子表格附件。
内容的提问来源于stack exchange,提问作者Ludogiraud
相关产品推荐
相关产品推荐

