Получить python getaddresses () для декодирования кодированных слов - PullRequest
3 голосов
/ 11 ноября 2010
msg = \
"""To: =?ISO-8859-1?Q?Caren_K=F8lter?= <ck@example.dk>, bob@example.com
Cc: "James =?ISO-8859-1?Q?K=F8lter?=" <jk@example.dk>
Subject: hello

message body blah blah blah

"""

import email.parser, email.utils
import itertools


parser = email.parser.Parser()
parsed_message = parser.parsestr(msg)

address_fields = ('to', 'cc')
addresses = itertools.chain(*(parsed_message.get_all(field) for field in address_fields if parsed_message.has_key(field)))
address_list = set(email.utils.getaddresses(addresses))


print address_list

Похоже, что email.utils.getaddresses () не обрабатывает MIME RFC 2047 в полях адреса автоматически.

Как получить ожидаемый результат ниже?

фактический результат:

set([('', 'bob@example.com'), ('=?ISO-8859-1?Q?Caren_K=F8lter?=', 'ck@example.dk'), ('James =?ISO-8859-1?Q?K=F8lter?=', 'jk@example.dk')])

желаемый результат:

set([('', 'bob@example.com'), (u'Caren_K\xf8lter', 'ck@example.dk'), (u'James \xf8lter', 'jk@example.dk')])

Ответы [ 3 ]

3 голосов
/ 11 ноября 2010

Требуемая функция: email.header.decode_header, которая возвращает список пар (decoded_string, charset). От вас зависит дальнейшее их декодирование в соответствии с charset и соединение их снова вместе, прежде чем передать их в email.utils.getaddresses или куда-либо еще.

Вы можете подумать, что это будет просто:

def decode_rfc2047_header(h):
    return ' '.join(s.decode(charset or 'ascii')
                   for s, charset in email.header.decode_header(h))

Но поскольку заголовки сообщений обычно поступают из ненадежных источников, вам приходится обрабатывать (1) плохо закодированные данные; и (2) поддельные имена наборов символов. Так что вы можете сделать что-то вроде этого:

def decode_safely(s, charset='ascii'):
    """Return s decoded according to charset, but do so safely."""
    try:
        return s.decode(charset or 'ascii', 'replace')
    except LookupError: # bogus charset
        return s.decode('ascii', 'replace')

def decode_rfc2047_header(h):
    return ' '.join(decode_safely(s, charset)
                   for s, charset in email.header.decode_header(h))
1 голос
/ 11 ноября 2010

Да, интерфейс пакета email действительно не очень полезен в большинстве случаев.

Здесь вы должны использовать email.header.decode_header вручную для каждого адреса, а затем, поскольку это дает вам список декодированных токенов, вы должны снова соединить их вручную:

for name, address in email.utils.getaddresses(addresses):
    name= u' '.join(
        unicode(b, e or 'ascii') for b, e in email.header.decode_header(name)
    )
    ...
0 голосов
/ 11 апреля 2012

Спасибо, Гарет Рис. Ваш ответ был полезен при решении проблемного случая:

Input: 'application/octet-stream;\r\n\tname="=?utf-8?B?KFVTTXMpX0FSTE8uanBn?="'

Отсутствие пробела вокруг закодированного слова заставило email.Header.decode_header его пропустить. Я слишком новичок в этом, чтобы знать, что я только усугубил ситуацию, но этот клудж, наряду с соединением с «вместо», исправил это:

if not ' =?' in h:
    h = h.replace('=?', ' =?').replace('?=', '?= ')

Output: u'application/octet-stream; name="(USMs)_ARLO.jpg"' 
...