Как я могу получить текст сообщения электронной почты, используя Python?
Учитывая сообщение RFC822 в Python 2.6, как я могу получить правильную часть текста/простого содержимого? В основном, алгоритм, который я хочу, заключается в следующем:
message = email.message_from_string(raw_message)
if has_mime_part(message, "text/plain"):
mime_part = get_mime_part(message, "text/plain")
text_content = decode_mime_part(mime_part)
elif has_mime_part(message, "text/html"):
mime_part = get_mime_part(message, "text/html")
html = decode_mime_part(mime_part)
text_content = render_html_to_plaintext(html)
else:
# fallback
text_content = str(message)
return text_content
Из этих вещей у меня есть get_mime_part
и has_mime_part
, но я не совсем уверен, как получить декодированный текст из части MIME. Я могу получить закодированный текст, используя get_payload()
, но если я пытаюсь использовать параметр decode
метода get_payload()
(см. документ), я получаю ошибку, когда вызываю его в тексте/обычная часть:
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/
email/message.py", line 189, in get_payload
raise TypeError('Expected list, got %s' % type(self._payload))
TypeError: Expected list, got <type 'str'>
Кроме того, я не знаю, как воспринимать HTML и как можно точнее отображать его в тексте.
Ответы
Ответ 1
В многостраничной электронной почте email.message.Message.get_payload()
возвращает список с одним элементом для каждой части. Самый простой способ - пройти сообщение и получить полезную нагрузку на каждую часть:
import email
msg = email.message_from_string(raw_message)
for part in msg.walk():
# each part is a either non-multipart, or another multipart message
# that contains further parts... Message is organized like a tree
if part.get_content_type() == 'text/plain':
print part.get_payload() # prints the raw text
Для не-многочастного сообщения не нужно делать все хождение. Вы можете перейти прямо к get_payload(), независимо от content_type.
msg = email.message_from_string(raw_message)
msg.get_payload()
Если содержимое закодировано, вам необходимо передать None
в качестве первого параметра в get_payload()
, за которым следует True (флаг декодирования является вторым параметром). Например, предположим, что мое электронное письмо содержит вложение документа MS Word:
msg = email.message_from_string(raw_message)
for part in msg.walk():
if part.get_content_type() == 'application/msword':
name = part.get_param('name') or 'MyDoc.doc'
f = open(name, 'wb')
f.write(part.get_payload(None, True)) # You need None as the first param
# because part.is_multipart()
# is False
f.close()
Что касается получения разумного простого текстового приближения части HTML, я обнаружил, что html2text работает довольно хорошо.
Ответ 2
Плоский лучше, чем вложенный;)
from email.mime.multipart import MIMEMultipart
assert isinstance(msg, MIMEMultipart)
for _ in [k.get_payload() for k in msg.walk() if k.get_content_type() == 'text/plain']:
print _
Ответ 3
Чтобы добавить @Jarret Hardie отличный ответ:
Мне лично нравится преобразовывать такие структуры данных в словарь, который я могу использовать позже, поэтому что-то вроде этого, где content_type
является ключом, а payload
является значением:
import email
[...]
email_message = {
part.get_content_type(): part.get_payload()
for part in email.message_from_bytes(raw_email).walk()
}
print(email_message["text/plain"])