Python - декодирование заголовка электронной почты UTF-8
существует ли какой-либо модуль Python, который помогает декодировать различные формы закодированных заголовков почты, в основном Subject, простым словам - строки 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?=
текст - закодированное sting - текст
текстовая строка
текст - закодированная строка - закодированная строка
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 )
Выводит
DEBUG: root: Subject is [('[201101251025] ELStAM;? = UTF-8 В IFZlcmbDvGd1bmcgdm9tIDIx =??. Januar 2011 ', None)]
что действительно не помогает.
Обновление 2:
Спасибо Ingmar Hupp в комментариях.
первый пример декодирует список из двух тэгелей:
print decode_header ( "" [201105161048] GewSt:?? = UTF-8 В IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0 = "" )
[('[201105161048] GewSt:', None), ('Wegfall der Vorl\xc3\xa4ufigkeit', 'UTF-8')]
это всегда [(string, encoding), (string, encoding),...], поэтому мне нужен цикл для конкатюции всех элементов [0] в одну строку или как получить все это в одной строке?
Тема: [201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011
не хорошо декодируется:
print decode_header ( "" [201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011 "" ")
[('[201101251025] ELStAM; =? UTF-8? B? IFZlcmbDvGd1bmcgdm9tIDIx? =. Januar 2011', None)]
Ответы
Ответ 1
Этот тип кодирования известен как MIME-кодированное слово и email модуль может декодировать его:
from email.header import decode_header
print decode_header("""=?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?=""")
Здесь выводится список кортежей, содержащий декодированную строку и используемую кодировку. Это связано с тем, что формат поддерживает разные кодировки в одном заголовке. Чтобы объединить их в одну строку, вам необходимо преобразовать их в общую кодировку и затем объединить ее, что может быть выполнено с помощью юникодного объекта 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 не расшифровывается:
Subject: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011
^
На самом деле это ошибка отправителей, которая нарушает требование кодированных слов в заголовке, разделяемом пробелом, указанном в RFC 2047, раздел 5, пункт 1: "закодированное слово", которое появляется в поле заголовка, определяемом как "текст", ДОЛЖНО быть отделено от любого смежного "закодированного слова" или "текста" на "линейно-белое пространство".
Если это необходимо, вы можете обойти это, предварительно обработав эти коррумпированные заголовки с помощью регулярного выражения, которое вставляет пробел после части кодированного слова (если это не конец), например:
import re
header_value = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", header_value)
Ответ 2
Я просто тестировал с закодированными заголовками в 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?='
Ответ 3
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)))
Ответ 4
Как насчет заголовков декодирования следующим образом:
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)
это получает предмет из электронной почты и декодирует его с указанной кодировкой (или без декодирования, если для кодировки установлено значение Нет).
Работала для меня для кодировок, установленных как "Нет", "utf-8", "koi8-r", "cp1251", "windows-1251"
Ответ 5
Этот script отлично работает для меня.. Я использую этот script для декодирования всех объектов электронной почты
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?=
Ответ 6
У Python есть электронная почта.
http://docs.python.org/library/email.header.html
Взгляните на email.header.decode_header()
Ответ 7
У меня была аналогичная проблема, но мой случай был немного другим:
- Python 3.5 (вопрос с 2011 года, но все еще очень высокий в google)
- Чтение сообщения непосредственно из файла в виде байтовой строки
Теперь классной особенностью python 3 email.parser является то, что все заголовки автоматически декодируются в 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), как уже описано в ответе от Ingmar Hupp., Поэтому мой ответ вдохновлен его.
Решение 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
Объяснение важных частей:
Я модифицировал регулярное выражение Ingmar Hupp только для замены неправильных MIME-кодированных слов: (=\?.*\?=)(?=\S)
Demuggex Demo. Потому что для всех было бы сильно замедлить синтаксический анализ (парсинг около 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)]
Для повторного декодирования последовательностей, экранированных unicode, мы устанавливаем default_charset = 'unicode-escape'
при создании нового значения заголовка.
Теперь correct_header_value
:
Re: 「コア会議」 (1/9(金 ) 6:00pm-7:00pm) のお知らせ'
Надеюсь, это немного сэкономит кому-нибудь.
Дополнение: ответ Sander Steffann мне не помог, потому что я не смог получить исходную ценность поля заголовка из класс сообщений.