web-dev-qa-db-ja.com

Python-UTF-8をデコードするメールヘッダー

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)]

34
Hans Moser

このタイプのエンコーディングは 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 ])

アップデート2:

この件名行のデコードされない問題:

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)
48
Ingmar Hupp

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?='
36
Sander Steffann
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)))
7
Vitaly Greck

次の方法でヘッダーをデコードする方法は次のとおりです。

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」として設定されたエンコーディングで私のために働いた

3
Eugene

私は同様の問題を抱えていましたが、私の場合は少し異なっていました:

  • Python 3.5(質問は2011年のものですが、グーグルでは非常に高いです)
  • ファイルからメッセージをバイト文字列として直接読み取る

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による回答 は、メッセージクラスからヘッダーフィールドの生の値を取得することができなかったため、実際には役に立ちませんでした。

1
Luke

このスクリプトは私には問題なく動作します。このスクリプトを使用して、すべての電子メールの件名をデコードします

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?=
1
Jescolabcn

Pythonには電子メールライブラリがあります。 http://docs.python.org/library/email.header.html

Email.header.decode_header()をご覧ください

0
acron