Pythonモジュールは、エンコードされたメールヘッダーのさまざまな形式、主にSubject、単純な-たとえば-UTF-8文字列のデコードに役立ちますか?
ここに私が持っているメールファイルからのSubjectヘッダーの例があります:
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に感謝します。
最初の例は、2つのtupelのリストにデコードします。
print decode_header( "" "[201105161048] GewSt:=?UTF-8?B?IFdlZ2ZhbGwgZGVyIFZvcmzDpHVmaWdrZWl0?=" "")
[( '[201105161048] GewSt:'、なし)、( 'Wegfall der Vorl\xc3\xa4ufigkeit'、 'utf-8')]
これは常に[(string、encoding)、(string、encoding)、...]なので、すべての[0]アイテムを1つの文字列に連結するループが必要ですか、それとも1つの文字列ですべてを取得する方法ですか?
件名:[201101251025] ELStAM; =?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=。 2011年1月
うまくデコードできません:
print decode_header( "" "[201101251025] ELStAM; =?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=。Januar 2011" "")
[( '[201101251025] ELStAM; =?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=。Januar 2011'、None)]
このタイプのエンコーディングは MIME encoded-Word として知られており、 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 ])
この件名行のデコードされない問題:
Subject: [ 201101251025 ] ELStAM;=?UTF-8?B?IFZlcmbDvGd1bmcgdm9tIDIx?=. Januar 2011
^
実際には送信者の障害であり、ヘッダー内のエンコードされた単語が空白で区切られているという要件に違反しており、 RFC 2047、セクション5、段落1 :「* text」として定義されたヘッダーフィールドに表示される「encoded-Word」は、「linear-white-space」によって隣接する「encoded-Word」または「text」から分離する必要があります。
必要に応じて、次のように、エンコードされたWord部分の後に空白を挿入する正規表現でこれらの破損したヘッダーを前処理することで、この問題を回避できます。
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ヘッダー部分を別々に保持します。
>>> 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) のお知らせ
問題は Ingmar Huppによる回答 で既に説明されているように、RFC 2047(MIMEでエンコードされたWordの後に行空白が必要です)に違反しています。だから私の答えは彼に触発されました。
解決策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)]
ユニコードでエスケープされたシーケンスを再デコードするには、新しいヘッダー値を構築するときに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?=
Pythonには電子メールライブラリがあります。 http://docs.python.org/library/email.header.html
Email.header.decode_header()をご覧ください