[英]Python - email header decoding UTF-8
是否有任何 Python 模块可以帮助将各种形式的编码邮件标头(主要是主题)解码为简单的 - 例如 - 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?=
文本 - 编码的刺痛 - 文本
文本 - 编码字符串
文本 - 编码字符串 - 编码字符串
Encodig 也可以是 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 是 [('[ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011', None)]
这并没有真正的帮助。
更新 2:感谢 Ingmar Hupp 在评论中。
第一个示例解码为两个元组的列表:
打印 decode_header("""[201105161048] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?=""")
[('[ 201105161048 ] GewSt:', None), (' Wegfall der Vorl\\xc3\\xa4ufigkeit', 'utf-8')]
这总是 [(string, encoding),(string, encoding), ...] 所以我需要一个循环来将所有 [0] 项连接到一个字符串中,或者如何将它们全部放在一个字符串中?
主题:[ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=。 2011 年 1 月
解码不好:
打印 decode_header("""[ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=.Januar 2011""")
[('[ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=.Januar 2011', None)]
这种类型的编码称为MIME 编码字,电子邮件模块可以对其进行解码:
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 ])
此主题行未解码的问题:
Subject: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011
^
实际上是发件人的错误,这违反了RFC 2047 第 5 节第 1 段中指定的标头中由空格分隔的编码字的要求:出现在定义为 ' 的标头字段中的“编码字” *text' 必须通过 'linear-white-space' 与任何相邻的 'encoded-word' 或 'text' 分开。
如果需要,您可以通过使用正则表达式预处理这些损坏的标头来解决此问题,该正则表达式在编码字部分之后插入一个空格(除非它在末尾),如下所示:
import re
header_value = re.sub(r"(=\?.*\?=)(?!$)", r"\1 ", header_value)
我只是在 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?='
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)))
如何以以下方式解码标头:
import poplib, email
from email.header import decode_header, make_header
...
subject, encoding = decode_header(message.get('subject'))[0]
if encoding==None:
print "\n%s (%s)\n"%(subject, encoding)
else:
print "\n%s (%s)\n"%(subject.decode(encoding), encoding)
这从电子邮件中获取主题并使用指定的编码对其进行解码(如果编码设置为无,则不进行解码)。
为我工作的编码设置为“无”、“utf-8”、“koi8-r”、“cp1251”、“windows-1251”
我有一个类似的问题,但我的情况有点不同:
现在 python 3 email.parser 的一个很酷的特性是所有的头都自动解码为 Unicode 字符串。 然而,这在处理错误的标题时会导致一些“不幸”。 因此,以下标题导致了问题:
Subject: Re: =?ISO-2022-JP?B?GyRCIVYlMyUiMnE1RCFXGyhC?=
(1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm)
=?ISO-2022-JP?B?GyRCJE4kKkNOJGkkOxsoQg==?=
这导致了以下msg['subject']
:
Re: 「コア会議」 (1/9(=?ISO-2022-JP?B?GyRCNmIbKEI=?=) 6:00pm-7:00pm) のお知らせ
好吧,问题是不符合 RFC 2047(在 MIME 编码字之后应该有一个行空白),如Ingmar Hupp的回答中所述。 所以我的回答受到了他的启发。
解决方案 1:在实际解析电子邮件之前修复字节字符串。 这似乎是更好的解决方案,但是我正在努力在字节字符串上实现正则表达式替换。 所以我选择了解决方案2:
解决方案 2:修复已解析和部分解码的标头值:
with open(file, 'rb') as fp: # read as byte-string
msg = email.message_from_binary_file(fp, policy=policy.default)
subject_fixed = fix_wrong_encoded_words_header(msg['subject'])
def fix_wrong_encoded_words_header(header_value):
fixed_header_value = re.sub(r"(=\?.*\?=)(?=\S)", r"\1 ", header_value)
if fixed_header_value == header_value: # nothing needed to fix
return header_value
else:
dh = decode_header(fixed_header_value)
default_charset = 'unicode-escape'
correct_header_value = ''.join([str(t[0], t[1] or default_charset) for t in dh])
return correct_header_value
重要部分说明:
我修改了 Ingmar Hupp 的正则表达式以仅替换错误的 MIME 编码词: (=\\?.*\\?=)(?=\\S)
Debuggex Demo 。 因为为所有人做会严重减慢解析速度(解析大约 150'000 封邮件)。
将decode_header
函数应用于fixed_header
,我们在dh
有以下部分:
dh == [(b'Re: \\u300c\\u30b3\\u30a2\\u4f1a\\u8b70\\u300d (1/9(', None),
(b'\x1b$B6b\x1b(B', 'iso-2022-jp'),
(b' ) 6:00pm-7:00pm) \\u306e\\u304a\\u77e5\\u3089\\u305b', None)]
为了重新解码 unicode 转义序列,我们在构建新的 header-value 时设置default_charset = 'unicode-escape'
。
correct_header_value
现在是:
Re: 「コア会議」 (1/9(金 ) 6:00pm-7:00pm) のお知らせ'
我希望这会为某人节省一些时间。
补充: Sander Steffann的回答并没有真正帮助我,因为我无法从消息类中获取标头字段的原始值。
这个脚本对我来说很好用..我用这个脚本来解码所有的电子邮件主题
pat2=re.compile(r'(([^=]*)=\?([^\?]*)\?([BbQq])\?([^\?]*)\?=([^=]*))',re.IGNORECASE)
def decodev2(a):
data=pat2.findall(a)
line=[]
if data:
for g in data:
(raw,extra1,encoding,method,string,extra)=g
extra1=extra1.replace('\r','').replace('\n','').strip()
if len(extra1)>0:
line.append(extra1)
if method.lower()=='q':
string=quopri.decodestring(string)
string=string.replace("_"," ").strip()
if method.lower()=='b':
string=base64.b64decode(string)
line.append(string.decode(encoding,errors='ignore'))
extra=extra.replace('\r','').replace('\n','').strip()
if len(extra)>0:
line.append(extra)
return "".join(line)
else:
return a
样品:
=?iso-8859-1?q?una-al-dia_=2806/04/2017=29_Google_soluciona_102_vulnerabi?=
=?iso-8859-1?q?lidades_en_Android?=
=?UTF-8?Q?Al=C3=A9grate?= : =?UTF-8?Q?=20La=20compra=20de=20tu=20vehi?= =?UTF-8?Q?culo=20en=20tan=20s=C3=B3lo=2024h?= =?UTF-8?Q?=2E=2E=2E=20=C2=A1Valoraci=C3=B3n=20=26?= =?UTF-8?Q?ago=20=C2=A0inmediato=21?=
from email.header import decode_header
mail = email.message_from_bytes(data[0][1])
subject_list = decode_header(mail['subject'])
sub_list = []
for subject in subject_list:
if subject[1]:
subject = (subject[0].decode(subject[1]))
elif type(subject[0]) == bytes:
subject = subject[0].decode('utf-8')
else:
subject = subject[0]
sub_list.append(subject)
subject = ''.join(sub_list)
print('Subject:' + subject)
对我来说,这很完美(并且总是给我一个字符串):
dmsgsubject, dmsgsubjectencoding = email.header.decode_header(msg['Subject'])[0]
msgsubject = dmsgsubject.decode(*([dmsgsubjectencoding] if dmsgsubjectencoding else [])) if isinstance(dmsgsubject, bytes) else dmsgsubject
Python 有一个电子邮件库。 http://docs.python.org/library/email.header.html
看看 email.header.decode_header()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.