让Python的getaddresses()解码编码词编码

3 投票
3 回答
1320 浏览
提问于 2025-04-16 06:55
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 个回答

0

感谢Gareth Rees。你的回答对解决我的问题很有帮助:

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"' 
1

是的,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)
    )
    ...
3

你需要的功能是 email.header.decode_header,这个函数会返回一个包含 (解码后的字符串, 字符集) 的列表。接下来,你需要根据 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))

撰写回答