Python - расшифровка заголовка письма UTF-8 - PullRequest
34 голосов
/ 07 сентября 2011

существует ли какой-либо модуль Python, который помогает декодировать различные формы закодированных заголовков почты, в основном, подчиненных простым, скажем, строкам UTF-8?

Вот примеры заголовков тем из моих почтовых файлов:

Subject: [ 201105311136 ]=?UTF-8?B?IMKnIDE2NSBBYnM=?=. 1 AO;
Subject: [ 201105161048 ] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?=
Subject: [ 201105191633 ]
  =?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?=
  =?UTF-8?B?Z2VuIGVpbmVzIFNlZW1hbm5z?=

текст - закодированный текст - текст

текст - закодированная строка

текст - закодированная строка - закодированная строка

Encodig также может быть чем-то вроде ISO 8859-15.

Обновление 1: я забыл упомянуть, я пробовал email.header.decode_header

    for item in message.items():
    if item[0] == 'Subject':
            sub = email.header.decode_header(item[1])
            logging.debug( 'Subject is %s' %  sub )

Это выводит

ОТЛАДКА: корень: Тема [['[201101251025] ELStAM;? = UTF-8 В IFZlcmbDvGd1bmcgdm9tIDIx =??. Январь 2011 ', нет)]

что не очень помогает.

Обновление 2: Спасибо Ингмару Хаппу в комментариях.

первый пример декодирует в список из двух наборов:

print decode_header ("" "[201105161048] GewSt:??? = UTF-8 В IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0 = "" ")
[('[201105161048] GewSt:', Нет), ('Wegfall der Vorl \ xc3 \ xa4ufigkeit', 'UTF-8')]

всегда ли это [(строка, кодировка), (строка, кодировка), ...], поэтому мне нужен цикл для объединения всех [0] элементов в одну строку или как получить все это в одной строке?

Тема: [201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Январь 2011

плохо декодирует:

print decode_header ("" "[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Январь 2011" "")

[('[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Январь 2011', нет)]

Ответы [ 8 ]

48 голосов
/ 07 сентября 2011

Этот тип кодировки известен как MIME кодированное слово , и модуль email может декодировать его:

from email.header import decode_header
print decode_header("""=?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?=""")

Выводит список кортежей, содержащий декодированную строку и используемую кодировку. Это потому, что формат поддерживает разные кодировки в одном заголовке. Чтобы объединить их в одну строку, вам необходимо преобразовать их в общую кодировку, а затем объединить это, что можно сделать с помощью объекта Unicode в Python:

from email.header import decode_header
dh = decode_header("""[ 201105161048 ] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?=""")
default_charset = 'ASCII'
print ''.join([ unicode(t[0], t[1] or default_charset) for t in dh ])

Обновление 2:

Проблема с тем, что эта строка темы не декодируется:

Subject: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011
                                                                     ^

На самом деле это ошибка отправителя, которая нарушает требование кодирования слов в заголовке, разделяемых пробелом, указанных в RFC 2047, раздел 5, параграф 1 : an 'закодировано -word ', которое появляется в поле заголовка, определенном как' * text ', ДОЛЖНО быть отделено от любого смежного' encoded-word 'или' text 'с помощью' linear-white-space '.

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

import re
header_value = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", header_value)
34 голосов
/ 12 февраля 2014

Я только что тестировал с закодированными заголовками в Python 3.3 и обнаружил, что это очень удобный способ справиться с ними:

>>> from email.header import Header, decode_header, make_header

>>> subject = '[ 201105161048 ] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?='
>>> h = make_header(decode_header(subject))
>>> str(h)
'[ 201105161048 ] GewSt:  Wegfall der Vorläufigkeit'

Как вы можете видеть, он автоматически добавляет пробел вокруг закодированных слов.

Он внутренне хранит кодированные и ASCII-заголовки разделенными, как вы можете видеть, когда перекодирует не-ASCII-части:

>>> h.encode()
'[ 201105161048 ] GewSt: =?utf-8?q?_Wegfall_der_Vorl=C3=A4ufigkeit?='

Если вы хотите, чтобы весь заголовок перекодировалсяВы можете преобразовать заголовок в строку и затем обратно в заголовок:

>>> h2 = Header(str(h))
>>> str(h2)
'[ 201105161048 ] GewSt:  Wegfall der Vorläufigkeit'
>>> h2.encode()
'=?utf-8?q?=5B_201105161048_=5D_GewSt=3A__Wegfall_der_Vorl=C3=A4ufigkeit?='
7 голосов
/ 22 августа 2012
def decode_header(value):
    return ' '.join((item[0].decode(item[1] or 'utf-8').encode('utf-8') for item in email.header.decode_header(value)))
3 голосов
/ 21 февраля 2012

Как насчет декодирования заголовков следующим образом:

import poplib, email

from email.header import decode_header, make_header

...

        subject, encoding = decode_header(message.get('subject'))[0]

        if encoding==None:
            print "\n%s (%s)\n"%(subject, encoding)
        else:
            print "\n%s (%s)\n"%(subject.decode(encoding), encoding)

это получает тему из электронной почты и декодирует ее с указанной кодировкой (или без декодирования, если кодировка установлена ​​на Нет).для меня для кодировок, заданных как 'None', 'utf-8', 'koi8-r', 'cp1251', 'windows-1251'

1 голос
/ 13 апреля 2017

Этот скрипт отлично работает для меня .. Я использую этот скрипт для декодирования всех тем письма

pat2=re.compile(r'(([^=]*)=\?([^\?]*)\?([BbQq])\?([^\?]*)\?=([^=]*))',re.IGNORECASE)

def decodev2(a):
    data=pat2.findall(a)
    line=[]
    if data:
            for g in data:
                    (raw,extra1,encoding,method,string,extra)=g
                    extra1=extra1.replace('\r','').replace('\n','').strip()
                    if len(extra1)>0:
                            line.append(extra1)
                    if method.lower()=='q':
                            string=quopri.decodestring(string)
                            string=string.replace("_"," ").strip()
                    if method.lower()=='b':
                            string=base64.b64decode(string)
                    line.append(string.decode(encoding,errors='ignore'))
                    extra=extra.replace('\r','').replace('\n','').strip()
                    if len(extra)>0:
                            line.append(extra)
            return "".join(line)
    else:
            return a

примеров:

=?iso-8859-1?q?una-al-dia_=2806/04/2017=29_Google_soluciona_102_vulnerabi?=
 =?iso-8859-1?q?lidades_en_Android?=

=?UTF-8?Q?Al=C3=A9grate?= : =?UTF-8?Q?=20La=20compra=20de=20tu=20vehi?= =?UTF-8?Q?culo=20en=20tan=20s=C3=B3lo=2024h?= =?UTF-8?Q?=2E=2E=2E=20=C2=A1Valoraci=C3=B3n=20=26?= =?UTF-8?Q?ago=20=C2=A0inmediato=21?=
1 голос
/ 06 декабря 2016

У меня была похожая проблема, но мой случай немного отличался:

  • Python 3.5 (вопрос с 2011 года, но все еще очень высоко в Google)
  • Чтениесообщение напрямую из файла в виде байтовой строки

Теперь классная особенность почтового ящика python 3 заключается в том, что все заголовки автоматически декодируются в строки Unicode-Strings.Однако это приводит к небольшому «несчастью» при работе с неправильными заголовками.Таким образом, следующий заголовок вызвал проблему:

Subject: Re: =?ISO-2022-JP?B?GyRCIVYlMyUiMnE1RCFXGyhC?=
 (1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm) 
 =?ISO-2022-JP?B?GyRCJE4kKkNOJGkkOxsoQg==?=

Это привело к следующему msg['subject']:

Re: 「コア会議」 (1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm)  のお知らせ

Ну, проблема не соответствует RFC 2047 (Должна быть белая строка- пробел после слова в кодировке MIME), как уже описано в ответе Ингмара Хаппа .Поэтому мой ответ вдохновлен его.

Решение 1: Исправьте строку байтов перед фактическим анализом электронной почты.Это казалось лучшим решением, однако я изо всех сил пытался реализовать подстановку Regex в байтовых строках.Поэтому я выбрал решение 2:

Решение 2: Исправьте уже проанализированное и частично декодированное значение заголовка:

with open(file, 'rb') as fp:  # read as byte-string
    msg = email.message_from_binary_file(fp, policy=policy.default)
    subject_fixed = fix_wrong_encoded_words_header(msg['subject'])


def fix_wrong_encoded_words_header(header_value):
    fixed_header_value = re.sub(r"(=\?.*\?=)(?=\S)", r"\1 ", header_value)

    if fixed_header_value == header_value:  # nothing needed to fix
        return header_value
    else:
        dh = decode_header(fixed_header_value) 
        default_charset = 'unicode-escape'
        correct_header_value = ''.join([str(t[0], t[1] or default_charset) for t in dh])
        return correct_header_value

Объяснение важных частей:

Я изменил регулярное выражение Ингмара Хаппа, чтобы заменить только неправильные слова в кодировке MIME: (=\?.*\?=)(?=\S) Демо Debuggex .Поскольку выполнение для всех сильно замедлит анализ (до 150 000 писем).

После применения функции decode_header к fixed_header в dh есть следующие части:

dh == [(b'Re: \\u300c\\u30b3\\u30a2\\u4f1a\\u8b70\\u300d (1/9(', None), 
       (b'\x1b$B6b\x1b(B', 'iso-2022-jp'), 
       (b' ) 6:00pm-7:00pm)  \\u306e\\u304a\\u77e5\\u3089\\u305b', None)]

Чтобы повторно декодировать экранированные Юникодом последовательности, мы устанавливаем default_charset = 'unicode-escape' при создании нового значения заголовка.

Теперь correct_header_value:

Re: 「コア会議」 (1/9(金 ) 6:00pm-7:00pm)  のお知らせ'

Надеюсь, это сэкономит кому-то время.

Дополнение: ответ Сандера Штеффана мне не очень помог, потому что я не смог получить исходную ценностьполе заголовка вне класса сообщений.

0 голосов
/ 14 февраля 2018

для меня это сработало идеально (а всегда дает мне строку):

dmsgsubject, dmsgsubjectencoding = email.header.decode_header(msg['Subject'])[0]
msgsubject = dmsgsubject.decode(*([dmsgsubjectencoding] if dmsgsubjectencoding else [])) if isinstance(dmsgsubject, bytes) else dmsgsubject
0 голосов
/ 07 сентября 2011

Python имеет электронную почту. http://docs.python.org/library/email.header.html

Взгляните на email.header.decode_header ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...