LoginSignup
23
31

More than 1 year has passed since last update.

Pythonでメール受信; poplibのサンプルコード

Last updated at Posted at 2018-10-18

関連リンク

処理の流れ

  1. POP3クライアントインスタンスを作成する
  2. POP3サーバにログインする
  3. メール受信(MIMEメッセージを受信)

サンプルコード

import base64
import email
import poplib
import ssl
from email.header import decode_header, make_header
# https://docs.python.jp/3/library/poplib.html

"""
(1) POP3クライアントインスタンスを作成する
* Yahoo!メールの場合
  host: "pop.mail.yahoo.co.jp"
  nego_combo: ("ssl", 995)
              (2021年1月のYahooメールのセキュリティ強化につき110ポートが廃止)

* Gmailの場合
  host: "pop.gmail.com"
  nego_combo: ("ssl", 995)
"""
host = "pop.gmail.com"
nego_combo = ("ssl", 995) # ("通信方式", port番号)

if nego_combo[0] == "no-encrypt":
    popclient = poplib.POP3(host, nego_combo[1], timeout=10)
elif nego_combo[0] == "starttls":
    context = ssl.create_default_context()
    popclient = poplib.POP3(host, nego_combo[1], timeout=10)
    popclient.stls(context)
elif nego_combo[0] == "ssl":
    context = ssl.create_default_context()
    # context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) うまくいかない場合はこちらも。
    popclient = poplib.POP3_SSL(host, nego_combo[1], timeout=10, context=context)
popclient.set_debuglevel(2)  # サーバとの通信のやりとりを出力してくれる


"""
(2) POP3サーバにログインする
"""
username = "username@gmail.com"
password = "password"
auth_method = "user"

if auth_method == "user":
    popclient.user(username)
    popclient.pass_(password)
elif auth_method == "apop":
    # Gmailはnot supported
    # yahooはnot supported
    popclient.apop(username, password)
elif auth_method == "rpop":
    # Gmailはできない
    # yahooはできない
    popclient.rpop(username)
    popclient.pass_(password)


"""
(3) メール受信(MIMEメッセージを受信)
"""
download_num = 3 # ダウンロードしたい数
msg_list = [] # 取得したMIMEメッセージを格納するリスト
msg_num = popclient.stat()[0]  # POP3サーバに存在するメールの数を取得
if msg_num <= download_num:
    download_num = msg_num
for i in range(download_num):
    msg_bytes = b""
    for line in popclient.retr(i+1)[1]:  # i+1から始まる(参考:https://docs.python.org/ja/3/library/poplib.html#:~:text=for%20j%20in%20M.retr(i%2B1)%5B1%5D%3A)
        msg_bytes += line + b"\n"
    msg_list.append(email.message_from_bytes(msg_bytes))
popclient.quit()


"""
受信したメール(MIMEメッセージ)の確認
"""
for msg in msg_list:
    print(msg)
    print()

"""
各ヘッダや本文を取得する
"""
for msg in msg_list:
    # 各ヘッダはディクショナリのようにアクセスできる
    from_addr = str(make_header(decode_header(msg["From"])))
    subject = str(make_header(decode_header(msg["Subject"])))
    print("From:{}".format(from_addr))
    print("Subject:{}".format(subject))

    # 本文(payload)を取得する
    if msg.is_multipart() is False:
        # SinglePartのとき
        payload = msg.get_payload(decode=True) # 備考の※1
        charset = msg.get_content_charset() # 備考の※2
        if charset is not None:
            payload = payload.decode(charset, "ignore")
        print(payload)
        print()
    else:
        # MultiPartのとき
        for part in msg.walk():
            payload = part.get_payload(decode=True)
            if payload is None:
                continue
            charset = part.get_content_charset()
            if charset is not None:
                payload = payload.decode(charset, "ignore")
            print(payload)
            print()


"""備考
※1 get_payload(decode=True)について
    cte = msg["Content-Transfer-Encoding"]や
    cte = part["Content-Transfer-Encoding"]
    でContent-Transfer-Encodingヘッダを取得してデコード方式を決定しても良いが、
    get_payload()のdecode引数をTrueにすると、
    Content-Transfer-Encodingヘッダに従って勝手にデコードしてくれる。
    ただしデコードしてくれるのはquoted-printable または base64のときのみで、
    それ以外の場合(別のエンコード方式や、ヘッダがない場合、ペイロード部が
    bogus dataを持っている場合 (i.e. bogus base64 or uuencoded data))のときはそのまま返す。
    * bogus dataって何だ?
      - MAILER-DAEMON@yahoo.co.jpのメールがデコードしきれずにpayloadがバイト列になったので、
        そういうのを指すのかもしれない。

※2 get_content_charset()について
    charsetがない不親切なメールがたまにあるので注意。
    気合でデコードするしかないが、今回のサンプルコードはそのようなメールは考慮していない。
"""
23
31
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
31