Python电子邮件包:如何可靠地将多部分消息转换/解码为

2024-03-29 02:37:28 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用Python处理旧的、可能不合规的电子邮件。我可以毫无疑问地在留言中读到:

In [1]: m=email.message_from_binary_file(open('/path/to/problematic:2,S',mode='rb'))

但随后将其转换为字符串时会出现UnicodeEncodeError:“gb2312”编解码器无法对1238位置的字符“\ufffd”进行编码:非法的多字节序列。这个有问题的消息的(多)部分有“Content Type:text/plain;charset=”gb2312“和“Content Transfer Encoding:8bit”。在

^{pr2}$

我不太熟悉电子邮件内部的特性,在网上搜索这种类型的错误时发现的主要问题是在抓取网页时,基本上有点明显的建议:读入的原始字节包含无法用目标编解码器编码的Unicode字符。在

我的问题是:什么是正确的方法来可靠地处理(可能不符合)电子邮件?在

编辑

有趣的是,m.get_payload(i=0).as_string()会触发相同的异常,但是m.get_payload(i=0).get_payload(decode=False)给出了在我的终端上正确显示的str,而{}给出了一个我无法解码的bytesb'\xd7\xaa...')。但是,错误发生在不同的字符上:

----> 1 m.get_payload(i=0).get_payload(decode=True).decode('gb2312')
UnicodeDecodeError: 'gb2312' codec can't decode byte 0xac in position 1995: illegal multibyte sequence

或者

----> 1 m.get_payload(i=0).get_payload(decode=True).decode('gb18030')
UnicodeDecodeError: 'gb18030' codec can't decode byte 0xa3 in position 2033: illegal multibyte sequence

Tags: true编码get字节电子邮件错误编解码器content
2条回答

简短的答案通常是error handlers在您的bytes.decode调用中。但细节取决于很多事情。在

首先,你想如何处理这些数据?通常你需要一些绝对可逆的东西,所以你可以保证在最坏的情况下你可以重新生成你所接受的东西,在这种情况下你可能想要surrogate-escape。在其他情况下,你想生成一些人类可读的东西,最好跳过不可能的mojibake,而不是试图呈现它,因此ignore可能是正确的答案。等等。在

第二,这是一个绝大多数消息都是好消息,但有少数消息是错误的,还是许多消息大多数都很好但有一些错误?在

最后,在某些情况下(对于传统的中文编码尤其如此),实际问题只是有人指定了一个密切相关的字符集,而不是他们实际使用的字符集。如果您看到的是这种情况,您可能需要尝试编写显式的后备代码:如果您遇到异常,请在常见错误的dict中查找编码,然后尝试其他编码。如果它们都不起作用,则返回到使用错误处理程序的特定编码。在

显然,如果Content-Transfer-Encoding8bitmessage.get_payload(decode=False)仍将尝试解码以恢复原始字节。另一方面,message.get_payload(decode=True)总是产生bytes,尽管实际解码只有在Content-Transfer-Encoding存在并且是quoted-printable或{}时才会发生。在

最后我得到了以下代码。不确定这是否是处理电子邮件的正确方法。在

body = []
if m.preamble is not None:
    body.extend(m.preamble.splitlines(keepends=True))

for part in m.walk():
    if part.is_multipart():
        continue

    ctype = part.get_content_type()
    cte = part.get_params(header='Content-Transfer-Encoding')
    if (ctype is not None and not ctype.startswith('text')) or \
       (cte is not None and cte[0][0].lower() == '8bit'):
        part_body = part.get_payload(decode=False)
    else:
        charset = part.get_content_charset()
        if charset is None or len(charset) == 0:
            charsets = ['ascii', 'utf-8']
        else:
            charsets = [charset]

        part_body = part.get_payload(decode=True)
        for enc in charsets:
            try:
                part_body = part_body.decode(enc)
                break
            except UnicodeDecodeError as ex:
                continue
            except LookupError as ex:
                continue
        else:
            part_body = part.get_payload(decode=False)

    body.extend(part_body.splitlines(keepends=True))

if m.epilogue is not None:
    body.extend(m.epilogue.splitlines(keepends=True))

相关问题 更多 >