Python - 邮件头UTF-8解码

58 投票
9 回答
61677 浏览
提问于 2025-04-17 01:39

有没有什么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?=

文本 - 编码字符串 - 文本

文本 - 编码字符串

文本 - 编码字符串 - 编码字符串

编码方式也可能是其他的,比如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?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011', None)]

但这并没有真正解决问题。

更新2:感谢评论区的Ingmar Hupp。

第一个例子解码后得到的是一个包含两个元组的列表:

> >>> print decode_header("""[ 201105161048 ]
> GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?=""")  
> [('[ 201105161048 ] GewSt:', None), (' Wegfall der Vorl\xc3\xa4ufigkeit',
> 'utf-8')]

这个格式总是[(字符串, 编码), (字符串, 编码), ...]吗?所以我需要一个循环把所有的[0]项连接成一个字符串,还是说有什么方法可以直接得到一个字符串呢?

> Subject: [ 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)]

9 个回答

6
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)))

当然可以!请把你想要翻译的内容发给我,我会帮你用简单易懂的语言解释清楚。

62

我在用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?='
63

这种编码方式被称为 MIME 编码词,而 email 模块可以解码它:

from email.header import decode_header
print decode_header("""=?UTF-8?B?IERyZWltb25hdHNmcmlzdCBmw7xyIFZlcnBmbGVndW5nc21laHJhdWZ3ZW5kdW4=?=""")

这个操作会输出一个包含解码字符串和所用编码的元组列表。之所以这样,是因为这种格式可以在同一个头部使用不同的编码。要把这些合并成一个字符串,你需要将它们转换成一种共同的编码,然后再把它们连接起来,这可以通过使用 Python 的 unicode 对象来实现:

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: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011
                                                                     ^

其实是发件人的问题,因为他们没有遵循编码词在头部中必须用空格分开的要求,这一点在 RFC 2047,第5节,第1段 中有说明:出现在定义为 '*text' 的头部字段中的 'encoded-word' 必须与任何相邻的 'encoded-word' 或 'text' 之间用 '线性空白' 分开。

如果需要,你可以通过使用正则表达式来预处理这些损坏的头部,在编码词部分后面插入一个空格(除非它在末尾),像这样:

import re
header_value = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", header_value)

撰写回答