[英]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.