通常のmailbox取得では、ファイルの全体をパース指定するようで、
とても時間がかかることは以前の記事に書いた。
import mailbox
mbox = mailbox.mbox('example.mbox')
for message in mbox:
print("Subject:", message['subject'])mbox.close()
逐次的にデータを読み込み、出力するように改善したく、
ChatGPTに聞いてみたが、出力されたソースコードでは、
messageオブジェクトが正しく作られない。
デバッグした結果、
message = email.message_from_bytes(b'\n'.join(lines), policy=default)
入力となるlinesの各要素に、すでに「\r\n」(改行)が入ってしまっており、
その状態でさらに「\n」(改行)でjoin()してしまっているため、
1行ごとに空行ができてしまい、1行目でメールヘッダが完結してしまうことが判明。
そのため、以下のように修正した。
# ***** modification starts here *****
#lines.append(line)
lines.append(line.rstrip(b'\r\n'))
# ***** modification ends here *****
これは「メールヘッダは最初の空行まで続く」ということを知っていて、
初めて分かるバグ。
以下、修正版の全体のコード。
読んだメールから内容を表示するので、とても機敏に動作する。
このソースをもとに、さらに調査を進めよう。
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-mboxfilename = 'category_social.mbox'
import email
from email.policy import default
from email.header import decode_headerclass MboxReader:
def __init__(self, filename):
self.handle = open(filename, 'rb')def __enter__(self):
return selfdef __exit__(self, exc_type, exc_value, exc_traceback):
self.handle.close()def __iter__(self):
return selfdef __next__(self):
lines = [ ]
while True:
line = self.handle.readline()
if line == b'' or line.startswith(b'From '):
message = email.message_from_bytes(b'\n'.join(lines), policy=default)
if message:
subject = message['subject']
if subject:
decoded_subject = decode_header(subject)[0]
if decoded_subject[1]:
subject = decoded_subject[0].decode(decoded_subject[1])
date = message['Date']
payload = message.get_payload()
if isinstance(payload, list):
payload = payload[0].get_payload() # Handle multipart messages
return subject, payload, date
if line == b'':
raise StopIteration
lines = []
continue
# ***** modification starts here *****
#lines.append(line)
lines.append(line.rstrip(b'\r\n'))
# ***** modification ends here *****with MboxReader(mboxfilename) as mbox:
for i, (subject, payload, date) in enumerate(mbox):
#if i > 100: break#print("Subject:", subject)
print(i, date, subject)
#print("Body:", payload)