Python IMAP消息获取UnicodeDecodeError';utf-8';编解码器可以';破译
经过5个小时的努力,是时候寻求帮助了。筛选了所有与此相关的stackoverflow问题,但找不到答案 该代码是一个gmail解析器——适用于大多数电子邮件,但有些电子邮件会导致UnicodeDecodeError。问题是“raw_email.decode('utf-8')”,但更改它(参见注释)会导致下面的另一个问题Python IMAP消息获取UnicodeDecodeError';utf-8';编解码器可以';破译,python,python-3.x,email,utf-8,imap,Python,Python 3.x,Email,Utf 8,Imap,经过5个小时的努力,是时候寻求帮助了。筛选了所有与此相关的stackoverflow问题,但找不到答案 该代码是一个gmail解析器——适用于大多数电子邮件,但有些电子邮件会导致UnicodeDecodeError。问题是“raw_email.decode('utf-8')”,但更改它(参见注释)会导致下面的另一个问题 # Source: https://stackoverflow.com/questions/7314942/python-imaplib-to-get-gmail-inbox-s
# Source: https://stackoverflow.com/questions/7314942/python-imaplib-to-get-gmail-inbox-subjects-titles-and-sender-name
import datetime
import time
import email
import imaplib
import mailbox
from vars import *
import re # to remove links from str
import string
EMAIL_ACCOUNT = 'gmail_login'
PASSWORD = 'gmail_psswd'
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(EMAIL_ACCOUNT, PASSWORD)
mail.list()
mail.select('inbox')
result, data = mail.uid('search', None, "ALL") # (ALL/UNSEEN)
id_list = data[0].split()
email_rev = reversed(id_list) # Returns a type list.reverseiterator, which is not list
email_list = list(email_rev)
i = len(email_list)
todays_date = time.strftime("%m/%d/%Y")
for x in range(i):
latest_email_uid = email_list[x]
result, email_data = mail.uid('fetch', latest_email_uid, '(RFC822)')
raw_email = email_data[0][1] # Returns a byte
raw_email_str = raw_email.decode('utf-8') # Returns a str
#raw_email_str = base64.b64decode(raw_email_str1) # Tried this but didn't work.
#raw_email_str = raw_email.decode('utf-8', errors='ignore') # Tried this but caused a TypeError down where var subject is created because something there is expecting a str or byte-like
email_message = email.message_from_string(raw_email_str)
date_tuple = email.utils.parsedate_tz(email_message['Date'])
date_short = f'{date_tuple[1]}/{date_tuple[2]}/{date_tuple[0]}'
# Header Details
if date_short == '12/23/2019':
#if date_tuple:
# local_date = datetime.datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
# local_message_date = "%s" %(str(local_date.strftime("%a, %d %b %Y %H:%M:%S")))
email_from = str(email.header.make_header(email.header.decode_header(email_message['From'])))
subject = str(email.header.make_header(email.header.decode_header(email_message['Subject'])))
#print(subject)
if email_from.find('restaurants@uber.com') != -1:
print('yay')
# Body details
if email_from.find('restaurants@uber.com') != -1 and subject.find('Payment Summary') != -1:
for part in email_message.walk():
if part.get_content_type() == "text/plain":
body = part.get_payload(decode=True)
body = body.decode("utf-8") # Convert byte to str
body = body.replace("\r\n", " ")
text = re.sub(r'\w+:\/{2}[\d\w-]+(\.[\d\w-]+)*(?:(?:\/[^\s/]*))*', '', body) # removes url links
text2 = text.translate(str.maketrans('', '', string.punctuation))
body_list = re.sub("[^\w]", " ", text2).split()
print(body_list)
print(date_short)
else:
continue
下面是一个示例,说明如何使用python标准库中的
imapclient
和email.*
模块检索和读取邮件部分:
from imapclient import IMAPClient
import email
from email import policy
def walk_parts(part, level=0):
print(' ' * 4 * level + part.get_content_type())
# do something with part content (applies encoding by default)
# part.get_content()
if part.is_multipart():
for part in part.get_payload():
get_parts(part, level + 1)
# context manager ensures the session is cleaned up
with IMAPClient(host="your_mail_host") as client:
client.login('user', 'password')
# select some folder
client.select_folder('INBOX')
# do something with folder, e.g. search & grab unseen mails
messages = client.search('UNSEEN')
for uid, message_data in client.fetch(messages, 'RFC822').items():
email_message = email.message_from_bytes(
message_data[b'RFC822'], policy=policy.default)
print(uid, email_message.get('From'), email_message.get('Subject'))
# alternatively search for specific mails
msgs = client.search(['SUBJECT', 'some subject'])
#
# do something with a specific mail:
#
# fetch a single mail with UID 12345
raw_mails = client.fetch([12345], 'RFC822')
# parse the mail (very expensive for big mails with attachments!)
mail = email.message_from_bytes(
raw_mails[12345][b'RFC822'], policy=policy.default)
# Now you have a python object representation of the mail and can dig
# into it. Since a mail can be composed of several subparts we have
# to walk the subparts.
# walk all parts at once
for part in mail.walk():
# do something with that part
print(part.get_content_type())
# or recurse yourself into sub parts until you find the interesting part
walk_parts(mail)
有关详细信息,请参阅文档。在这里,您可以找到所有需要的位来读取邮件信息。使用“ISO 8859-1”而不是“utf-8”可以让您的生活更轻松,您可能想看看。它处理的是最低级的东西,非常容易使用。除非您实现邮件和imap RFC的所有边缘情况(包括不同邮件消息部分的不同编码等),否则您上面的代码永远不会可靠工作。谢谢@jerch!惊人的资源和它的工作!但是,它没有显示如何提取电子邮件的正文,这正是我想要解析的。是的,imapclient会停下来帮助处理消息本身(原因很简单,一个大的附件会惩罚解析,这是不必要的)。要可靠地解析原始邮件消息,请参阅stdlib模块,如
email.message
和email.parser
()。遗憾的是,邮件消息可能很复杂(由于部分逻辑具有不同的编码和MIMEType),您必须通过文档来涵盖这些方面。谢谢@Jerch,您是否能够详细说明代码的外观?我尝试过各种形式的smtplib、imaplib、imapclient和电子邮件库,但都没有成功。我使用imaplib时非常接近,但无法找出问题所在。任何指导都会有帮助!尝试使用高级库:已解析所有内容。对于发件人声明错误内容传输编码的许多实际邮件,此操作仍可能失败。历史上,许多客户机声明了“us ascii”
,但还是发送了一些未声明的8位编码;如今,许多人可能声称“utf-8”,但实际上却使用了其他东西。没错,但情况总是这样——如果某个东西表示为XY,但表示为Z,则会出现更大的问题(需要更复杂的恢复策略,而且不能如此简单地设计蓝图)。