【问题标题】:How to get email.Header.decode_header to work with non-ASCII characters?如何让 email.Header.decode_header 使用非 ASCII 字符?
【发布时间】:2015-06-18 06:10:39
【问题描述】:

我借用以下代码来解析电子邮件标头,并进一步添加标头。诚然,我并不完全理解所有关于应该直接使用 email.Headers 模块的脚手架的原因。

值得注意的是Headers没有被实例化;而是调用它的decode_header 函数:

class DecodedHeader(object):
    def __init__(self, s, folder):
        self.msg=email.message_from_string(s[1])
        self.info=parseList(s[0])
        self.folder=folder

    def __getitem__(self,name):
        if name.lower()=='folder': return self.folder
        elif name.lower()=='uid': return self.info[1][3]
        elif name.lower()=='flags': return ','.join(self.info[1][1])
        elif name.lower()=='internal-date':
            ds= self.info[1][5]
            if Options.dateFormat:
                ds= time.strftime(Options.dateFormat,imaplib.Internaldate2tuple('INTERNALDATE "'+ds+'"'))
            return ds
        elif name.lower()=='size': return self.info[1][7]
        val= self.msg.__getitem__(name)
        if val==None: return None
        return self._convert(email.Header.decode_header(val),name)
    def get(self,key,default=None):
        return self.__getitem__(key)

    def _convert(self, list, name):
        l=[]
        for s, encoding in list:
            try:    
                if (encoding!=None):
                    s=unicode(s,encoding, 'replace').encode(Options.encoding,'replace')
            except Exception, e:
                print >>sys.stderr, "Encoding error", e
            l.append(s)

        res= "".join(l)
        if Options.addr and name.lower() in ('from','to', 'cc', 'return-path','reply-to' ): res=self._modifyAddr(res)
        if Options.dateFormat and name.lower() in ('date'): res = self._formatDate(res)
        return res  

问题来了:当标头(val)包含 Ä 和 ä 等非 ASCII 字符时,我得到:

Traceback (most recent call last):
  File "v12.py", line 434, in <module>
    main()
  File "v12.py", line 396, in main
    writer.writerow(msg)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/csv.py", line 152, in writerow
    return self.writer.writerow(self._dict_to_list(rowdict))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/csv.py", line 149, in _dict_to_list
    return [rowdict.get(key, self.restval) for key in self.fieldnames]
  File "v12.py", line 198, in get
    return self.__getitem__(key)
  File "v12.py", line 196, in __getitem__
    return self._convert(email.Header.decode_header(val),name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/email/header.py", line 76, in decode_header
    header = str(header)
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 1: ordinal not in range(128)

其中 u'\xe4' 是 ä。

我尝试了一些方法:

  • 在 header.py 的顶部添加 # -- coding: utf-8 --
  • 在将val 传递给decode_header() 之前调用unicode()
  • 在将val 传递给decode_header() 之前调用.encode('utf-8')
  • 在将val 传递给decode_header() 之前调用.encode('ISO-8859-1')

对上述任何一项都不满意。这是什么原因?鉴于我希望保持上述 email.Header 的用法(直接实例化 Header not),我们如何确保非 ASCII 字符被 decode_header 成功解码?

【问题讨论】:

  • 当您提供的示例难以运行时,尝试提供帮助会有点令人反感。您可能想查看 stackoverflow.com/help/mcve 。我会跳入并四处游荡以体验问题并尝试解决它来接近它。但是......需要一个比这更多的 MCVE 才能开始。
  • @GreenAsJade 公平点。然而,要包含其余代码以及调用的其他模块,只是为了让它运行,我们需要数百行代码。我原以为 Python 专家能够仅通过代码审查就能查明错误。就像我说的那样,我尝试了各种方法(见上文)并研究了其他几篇关于 email.Header 和编码/编码的 SO 帖子,但无济于事。
  • @GreenAsJade 以下是完整代码的链接:old.zderadicka.eu/mailexp.py 为了重现该问题,您需要拥有(或创建)一封电子邮件,其中至少有一个包含字符的标题像ä。
  • 问题是您要求我们做工作,所以您不必这样做。如果你让帮助者更容易自己做尽可能多的工作,你通常会得到更好的帮助。通常,您会发现,如果您自己编写一个导致问题的简单示例,您就会找到解决方案。如果你不这样做,那么它至少对我们来说很容易。发布整个代码的链接通常是没有用的,因为一大块代码并不明显如何运行。您需要的是一个将 unicode 传递给 decode_header 的小示例,与其他不相关的代码分开。
  • 这看起来像是针对此问题的 MCVE。它假设你可以将任何你喜欢的东西传递给decode_headers()。 import email.Header print email.Header.decode_header("a") print email.Header.decode_header(u'\xe4')

标签: python unicode utf-8 character-encoding non-ascii-characters


【解决方案1】:

标头必须正确编码才能被解码。看起来val 来自一个已经存在的消息,所以该消息可能是错误的。该错误表明它是一个 Unicode 字符串,但此时它应该是一个字节字符串。 email.header 的 Python 帮助中的示例很简单。

下面对两个甚至不使用相同编码的标头进行编码:

>>> import email.header
>>> h = email.header.Header(u'To: Märk'.encode('iso-8859-1'),'iso-8859-1')
>>> h.append(u'From: Jòhñ'.encode('utf8'),'utf8')
>>> h
<email.header.Header instance at 0x00559F58>
>>> s = h.encode()
>>> s
'=?iso-8859-1?q?To=3A_M=E4rk?= =?utf-8?b?RnJvbTogSsOyaMOx?='

请注意,正确编码的标头是嵌入了编码名称的字节字符串,并且不使用非 ASCII 字符。

这会解码它们:

>>> email.header.decode_header(s)
[('To: M\xe4rk', 'iso-8859-1'), ('From: J\xc3\xb2h\xc3\xb1', 'utf-8')]
>>> d = email.header.decode_header(s)
>>> for s,e in d:
...  print s.decode(e)
...
To: Märk
From: Jòhñ

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多