Python3 email modu中的错误解码

2024-05-23 17:53:09 发布

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

我最近遇到了一个EML文件,我想用Python电子邮件模块解析它。 在from标题中,有以下文本:

From: "=?utf-8?b?5b2t5Lul5Zu9L+esrOS6jOS6i+S4mumDqOmhueebrumDqC/nrKzkuozkuovkuJrp?=
=?utf-8?b?g6g=?=" <email@address.com>

所以名字被分为两部分。当我连接代码并将其手动解码为十六进制时,我得到以下结果,这是正确的UTF-8字符串:

^{pr2}$

但是,当我调用Python电子邮件解析器parse时,最后3个字节没有被正确解码。相反,当我读取message['from']的值时,有一些替代项:

dce9:20:dc83:dca8

例如,当我想打印字符串时,结果是

UnicodeEncodeError('utf-8', '彭以国/第二事业部项目部/第二事业\udce9\udc83\udca8', 17, 18, 'surrogates not allowed')

当我将From头中的两个编码部分合并为一个时,如下所示:

From: "=?utf-8?b?5b2t5Lul5Zu9L+esrOS6jOS6i+S4mumDqOmhueebrumDqC/nrKzkuozkuovkuJrpg6g=?=" <email@address.com>

这个字符串被库正确解码,可以很好地打印出来。在

这是Python电子邮件模块中的错误吗?EML标准是否允许双重编码值?在

在这里解码一个错误的Python文件时,实际上不会重新生成一个错误的代码

EML公司:

Content-Type: multipart/mixed; boundary="===============2193163039290138103=="
MIME-Version: 1.0
Date: Wed, 25 Aug 2018 19:21:23 +0100
From: "=?utf-8?b?5b2t5Lul5Zu9L+esrOS6jOS6i+S4mumDqOmhueebrumDqC/nrKzkuozkuovkuJrp?=
 =?utf-8?b?g6g=?=" <addr@addr.com>
Message-Id: <12312924463694945698.525C0AC435BA7D0E@xxxxx.com>
Subject: Sample subject
To: addr@addr.com

--===============2193163039290138103==
MIME-Version: 1.0
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64

VGhpcyBpcyBhIHNhbXBsZSB0ZXh0

--===============2193163039290138103==--

Python代码:

from email.parser import Parser
from email import policy
from sys import argv


with open(argv[1], 'r', encoding='utf-8') as eml_file:
    msg = Parser(policy=policy.default).parse(eml_file)

print(msg['from'])

结果:


Tags: 字符串代码fromcom电子邮件email错误content
1条回答
网友
1楼 · 发布于 2024-05-23 17:53:09

这似乎是email.parser基础设施如何处理包含From报头和其他结构化报头的编码单词标记的多行报头展开的问题。对于非结构化的头,例如Subject,它正确地做到了这一点。在

头部有两个encoded word部分,在两个单独的行上。这是非常正常的,一个编码的单词标记有有限的空间(有一个最大长度限制),因此您的UTF-8数据被分割成两个这样的单词,中间有一个行分隔符加上空格。一切都很好。无论生成的电子邮件是错误的,在UTF-8字符的中间分割(RFC2047规定是严格禁止的),此类数据的解码器不应在解码的字节之间插入空格。额外的空间会阻止email头处理连接代理项和修复数据。在

因此,这似乎是处理结构化头时解析头的方式上的一个缺陷;解析器不能正确处理编码单词之间的空格,这里的空格是由折叠的头行引入的。这会导致两个编码字部分之间的空间被保留,从而阻止了正确的解码。因此,虽然RFC2047声明编码字段必须包含整字符(多字节编码不能拆分),但它也声明编码字可以用CRLF空格分隔符拆分,并且编码字之间的任何空格都将被忽略。在

您可以通过提供一个自定义策略类来解决这个问题,该类从您自己的^{} method实现中的行中删除前导空格。在

import re
from email.policy import EmailPolicy

class UnfoldingEncodedStringHeaderPolicy(EmailPolicy):
    def header_fetch_parse(self, name, value):
        # remove any leading white space from header lines
        # that separates apparent encoded-word tokens before further processing 
        # using somewhat crude CRLF-FWS-between-encoded-word matching
        value = re.sub(r'(?<=\?=)((?:\r\n|[\r\n])[\t ]+)(?==\?)', '', value)
        return super().header_fetch_parse(name, value)

并将其作为加载时的策略:

^{pr2}$

演示:

>>> from io import StringIO
>>> from email.parser import Parser
>>> from email.policy import default as default_policy
>>> custom_policy = UnfoldingEncodedStringHeaderPolicy()
>>> Parser(policy=default_policy).parse(StringIO(data))['from']
'彭以国/第二事业部项目部/第二事业� �� <addr@addr.com>'
>>> Parser(policy=custom_policy).parse(StringIO(data))['from']
'彭以国/第二事业部项目部/第二事业部 <addr@addr.com>'

我归档了Python issue #35547来跟踪这个。在

相关问题 更多 >