質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.07%

PythonでContent-Transfer-Encoding: quoted-printableのメールを受信する方法

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,553

sandalwalk

score 58

Python3.6でgmailを受信&表示するプログラムを書いています。
ヘッダー情報で、
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64
となっているメールは以下のコードで解読できるのですが、ヘッダー情報が
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
のメールが読み込まれるとエラーが出てしまいます。
どうすれば、両者とも解読&表示することができるのでしょうか。

import imaplib 
import email
import base64

gmail = imaplib.IMAP4_SSL("imap.gmail.com")
gmail.login(“mymail@gmail.com”,”my password”)
gmail.select('INBOX',readonly=True)
typ, [data] = gmail.search(None,'(UNSEEN)')

for num in data.split():
    count+=1

    result, d = gmail.fetch(num,"(RFC822)")
    raw_email = d[0][1]
    msg = email.message_from_string(raw_email.decode('iso-2022-jp'))
    msg_encoding = email.header.decode_header(msg.get('Subject'))[0][1] or 'iso-2022-jp'


#本文の取得
    body = ""
    if msg.is_multipart():
        for payload in msg.get_payload():
            if payload.get_content_type() == "text/plain":
                body = payload.get_payload()
                if msg_encoding == 'utf-8':
                    body = base64.urlsafe_b64decode(body.encode('ASCII')).decode("utf-8")
    else:
        if msg.get_content_type() == "text/plain":
            body = msg.get_payload()
            if msg_encoding == 'utf-8':
                body = base64.urlsafe_b64decode(body.encode('ASCII')).decode("utf-8")
    print(body)
    print('')

gmail.close()
gmail.logout()
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

opyonさんのやり方でいけると思います。
cteの決め方は以下はどうでしょう。

msg_subject = msg.get('Subject').lower()

if b"?b?" in msg_subject:
    cte = "base64"
elif b"?q?" in msg_subject:
    cte = "quoted-printable"
else:
    cte = "ascii"

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/29 11:14 編集

    私のコード(ネット上の情報を参考に作成)もそうなのですが、本文のエンコード方式を知りたいのに、'Subject'に見に行くのはどうしてなのでしょうか?メールのヘッダーを見ると(下に例)、CTEはSubjectとは別の場所に書かれている様に思えて理解できていません。Content-Transfer-Encodingヘッダーを直接見に行く方法があれば教えて頂けますでしょうか。
    Subject: こんばんは~
    Cc: cc@example.com
    In-Reply-To: <CAA3QdPUTTwPuefb40LKXitkx=6N9D80-U1p3_jpe49Jzt5awxw@example.com>
    References: <20140620193439.7CBF.78EEE63C@example.com> <CAAwPuefb40LKX3QdPUTTitkx=6N9D80-U1p3_jpe49Jazt5wxw@example.com>
    Message-Id: <20140701162135.7CBF.9BE9E63C@example.com>
    MIME-Version: 1.0
    Content-Type: text/plain; charset="ISO-2022-JP"
    Content-Transfer-Encoding: 7bit

    キャンセル

  • 2018/09/29 11:26

    本文とSubjectは同じエンコード方式でエンコーディングされているとは限りません。

    ですので、本文のエンコード方式を知りたいときはContent-Typeヘッダのcharsetと、Content-Transfer-Encodingヘッダを確認します。

    Subjectのエンコード方式を知りたいときは、Subjectを解析します。
    例えば、
    b"Subject: =?UTF-8?Q?=E3=80=90=E3=83=A4=E3=..."
    とあれば、charsetはUTF-8で、「?Q?」がquoted-printable変換を意味しています。

    キャンセル

  • 2018/09/30 11:11

    いろいろなメールで試してみましたが、'Subject'のCTEはご指摘の方法で取得ができそうです。本文のCTEについては、実際のメール(少なくとも私に来るもの)の多くがマルチパートのメールで、その場合にはヘッダーだけを見て、cte = msg.get('Content-Transfer-Encoding')として、一発変換することはできない様です。このコードをマルチパートメールで使うと、本文中に書かれているcharsetとContent-Transfer-Encoding部分の情報とは異なる結果が得られてしまいます。

    キャンセル

  • 2018/09/30 14:25

    マルチパートの場合、パートごとにContent-TypeとContent-Transfer-Encodingが設定されていると思いますので、それぞれの場合でcteをセットし、そのパートをデコードするという処理になると思います。

    キャンセル

checkベストアンサー

0

検索しただけですがコードや解説見る限りお決まりの定型処理で出来るようですよ。

メールのデコード処理の流れ

-以下引用-
メール本文のデコード処理の流れ
受信したメールのメッセージには、Content-Transfer-EncodingヘッダとContent-typeヘッダが記載されています。 
デコード処理の流れとしては、まずContent-Transfer-Encodingヘッダを見てデコードし、次にContent-typeヘッダのcharsetを見て更にデコードします。

def decode_msg(msg, cte, charset):
    """ メール本文をデコードする
    * msg
      - メール本文(バイナリ形式)
    * cte(Content-Transfer-Encoding)
      - "base64"
      - "quoted-printable"
      - それ以外は、そのまま
    * charset(文字コード)
      - "utf-8"
      - "iso-2022-jp"
      - それ以外は、"ascii"とみなす
    """
    import quopri
    import base64

    if cte == "base64":
        b = base64.b64decode(msg)
    elif cte == "quoted-printable":
        b = quopri.decodestring(msg, header=False)
    else:
        b = msg
    if charset == "iso-2022-jp":
        return b.decode("iso-2022-jp", "ignore")
    elif charset == "utf-8":
        return b.decode("utf-8", "ignore")
    else:
        return b.decode("ascii", "ignore")

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.07%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る