Расшифровка темы письма в Thunderbird в Python 3.x - PullRequest
1 голос
/ 30 марта 2020

Обходной путь см. Ниже

/ Оригинальный вопрос:

Извините, я просто слишком туп, чтобы решить это самостоятельно , Я пытаюсь прочитать «предметы» из нескольких писем, хранящихся в папке .mbox от Thunderbird. Сейчас я пытаюсь декодировать заголовок с помощью decode_header(), но все еще получаю UnicodeErrors.

Я использую следующую функцию (я уверен, что есть более разумный способ сделать это, но это не так смысл этого поста)

import mailbox
from email.header import decode_header

mflder = mailbox.mbox("mailfolder")

for message in mflder:
    print(header_to_string(message["subject"]))

def header_to_string(header):
    try:
        header, encoding = decode_header(header)[0]
    except:
        return "something went wrong {}".format(header)
    if encoding == None:
        return header
    else:
        return header.decode(encoding)

Первые 100 выводов или около того совершенно нормальны, но затем появляется это сообщение об ошибке:

---------------------------------------------------------------------------
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-97-e252df04c215> in <module>
----> 1 for message in mflder:
      2     try:
      3         print(header_to_string(message["subject"]))
      4     except:
      5         print("0")

~\anaconda3\lib\mailbox.py in itervalues(self)
    107         for key in self.iterkeys():
    108             try:
--> 109                 value = self[key]
    110             except KeyError:
    111                 continue

~\anaconda3\lib\mailbox.py in __getitem__(self, key)
     71         """Return the keyed message; raise KeyError if it doesn't exist."""
     72         if not self._factory:
---> 73             return self.get_message(key)
     74         else:
     75             with contextlib.closing(self.get_file(key)) as file:

~\anaconda3\lib\mailbox.py in get_message(self, key)
    779         string = self._file.read(stop - self._file.tell())
    780         msg = self._message_factory(string.replace(linesep, b'\n'))
--> 781         msg.set_from(from_line[5:].decode('ascii'))
    782         return msg
    783 

UnicodeDecodeError: 'ascii' codec can't decode byte 0x93 in position 4: ordinal not in range(128)

Как я могу заставить mailbox.py декодировать другая кодировка? Или заголовок просто сломан? И если я правильно понял, заголовки должны быть "ASCII", верно? Я имею в виду, в этом смысл всей этой вещи MIME, нет?

Спасибо за вашу помощь!

/ Обходной путь

Я нашел обходной путь просто избегая прямой итерации по представлению почтовой папки .mbox. Вместо использования ...

for message in mflder:
    # do something

... просто используйте:

for x in range(len(mflder)):
    try:
        message = mflder[x]
        print(header_to_string(message["subject"]))
    except:
        print("Failed loading message!")

Это пропускает неработающие сообщения в папке .mbox. Тем не менее, я столкнулся с рядом других проблем при работе с темами папки .mbox. Например, заголовки иногда разделяются на несколько кортежей при использовании функции decode_header(). Таким образом, чтобы получить полный набор предметов, нужно добавить еще кое-что к функции header_to_string(). Но это больше не связано с этим вопросом. Я нуб и программист, но я помню, как работал с Outlook API и Python, что было НАМНОГО проще ...

1 Ответ

1 голос
/ 31 марта 2020

Решение

Похоже, вы либо испортили файл mbox "mailfolder", либо в модуле Python mailbox произошла ошибка, вызванная чем-то в вашем файле. Я не могу сказать, что происходит, не имея входного файла mbox или минимального входного файла примера, который воспроизводит проблему.

Вы можете сделать некоторую отладку самостоятельно. Каждое сообщение в файле начинается со строки «От», которая должна выглядеть следующим образом:

From - Mon Mar 30 18:18:04 2020

Из отправленной вами трассировки стека похоже, что эта строка искажена в одном из сообщений. Лично я бы использовал отладчик IDE (PyCharm), чтобы отследить, какой была неправильная строка, но это можно сделать с помощью встроенного в Python pdb. Оберните ваш l oop следующим образом:

import pdb
try:
    for message in mflder:
        print(header_to_string(message["subject"]))
except:
    pdb.post_mortem()

Когда вы запустите код сейчас, он появится в отладчике при возникновении исключения. В этом приглашении вы можете ввести l, чтобы отобразить код, в котором остановился отладчик; это должно соответствовать последнему кадру, напечатанному в вашей трассировке стека, которую вы первоначально разместили. Когда вы окажетесь там, есть две команды, которые скажут вам, что происходит:

p from_line

покажет вам искаженную строку «От».

p start

покажет вам на какое смещение в файле код mailbox считает, что сообщение должно быть.

Предыдущий ответ, который не решил исходную проблему, но по-прежнему применяется

В реальном мире быть сообщения, которые не соответствуют стандартам. Вы можете попытаться сделать код более терпимым, если не хотите отклонять плохие сообщения. Декодирование с помощью "latin-1" является одним из способов обработки этих заголовков байтами вне ASCII. Это не может завершиться ошибкой, поскольку все возможные значения байтов отображаются на действительные символы Unicode (сопоставление первых 256 кодов Unicode в формате «один к одному» против ISO / IEC 8859-1, или «латинский-1»). Это может или не может дать вам текст, предназначенный отправителю.

import mailbox
from email.header import decode_header

mflder = mailbox.mbox("mailfolder")

def get_subject(message):
    header = message["subject"]
    if not header:
        return ''
    header, encoding = decode_header(header)[0]
    if encoding is not None:
        try:
            header = header.decode(encoding)
        except:
            header = header.decode('latin-1')
    return header

for message in mflder:
    print(get_subject(message))
...