如何使用python解码此附件文件名?

时间:2015-03-11 11:42:31

标签: python decode email-attachments email-headers

我已经在我的代码中解码了很多电子邮件附件文件名。

但是这个特殊的文件名会破坏我的代码。

这是一个最小的例子:

from email.header import decode_header
encoded_filename='=?UTF-8?B?U2FsZXNJbnZvaWNl?==?UTF-8?B?LVJlcG9ydC5wZGY=?='
decoded_header=decode_header(encoded_filename) # --> [('SalesInvoiceQ1|\x04\xb5I\x95\xc1\xbd\xc9\xd0\xb9\xc1\x91\x98', 'utf-8')]
filename=str(decoded_header[0][0]).decode(decoded_header[0][1])

例外:

UnicodeDecodeError: 'utf8' codec can't decode byte 0xb5 in position 16: invalid start byte

不要问我怎么做,但Thunderbird能够将此文件名解码为:SalesInvoice-Report.pdf

我如何解决这个问题,就像电子邮件客户端显然可以解决这个问题一样?

1 个答案:

答案 0 :(得分:3)

该标头中有两个 Encoded-Word个部分。你必须检测一个结束的地方和一个开始的地方:

>>> print  decode_header(encoded_filename[:28])[0]
('SalesInvoice', 'utf-8')
>>> print  decode_header(encoded_filename[28:])[0]
('-Report.pdf', 'utf-8')

显然这是Thunderbird在这种情况下所做的事情;将字符串拆分为=?encoding?data?=个块。 通常这些字符应由\r\n(CARRIAGE RETURN + LINE FEED)字符分隔,但在您的情况下,它们会被拼凑在一起。如果您重新引入\r\n分隔符,则值会正确解码:

>>> decode_header(encoded_filename[:28] + '\r\n' + encoded_filename[28:])[0]
('SalesInvoice-Report.pdf', 'utf-8')

您可以使用正则表达式提取部件并重新引入分隔符:

import re
from email.header import decode_header

quopri_entry = re.compile(r'=\?[\w-]+\?[QB]\?[^?]+?\?=')

def decode_multiple(encoded, _pattern=quopri_entry):
    fixed = '\r\n'.join(_pattern.findall(encoded))
    output = [b.decode(c) for b, c in decode_header(fixed)]
    return ''.join(output)

演示:

>>> encoded_filename = '=?UTF-8?B?U2FsZXNJbnZvaWNl?==?UTF-8?B?LVJlcG9ydC5wZGY=?='
>>> decode_multiple(encoded_filename)
u'SalesInvoice-Report.pdf'

当然,它可能是你在首先阅读标题时遇到的错误。在提取\r\n值时,请确保您不会意外破坏现有的encoded_filename分隔符。