poplib簡介
poplib 是Python 3中的官方郵件庫,實現了POP的標準:RFC1939,用于郵件的收取。與之類似的還有imaplib 。
(注:本文僅拿pop舉例)
poplib的使用方法,就是幾步:
- 先創建一個
poplib.POP3
類的實例(如果使用SSL,則是poplib.POP3_SSL
類的實例) - 之后使用
user
、pass_
設置認證 - 再使用
list
獲取郵件列表 - 根據列表序號,使用
retr
收取郵件。 - (可選)使用
delete
刪除郵件。
另外,如果需要調試,可以使用類的set_debug
方法,打印詳細的報文交互過程。
如:
def get_mail(host, port, is_ssl, is_debug, username, password):if is_ssl:server = poplib.POP3_SSL(host, port)else:server = poplib.POP3(host, port)# 開啟debug模式,將會打印詳細的報文交互過程,便于調試server.set_debuglevel(is_debug)if is_debug:print(server.getwelcome().decode('utf-8'))server.user(username)server.pass_(password)if is_debug:print('Messages: %s. Size: %s' % server.stat())resp, mails, octets = server.list()if is_debug:print(mails)for index in range(1, len(mails) + 1, 1):# 下文將自定義自定義函數,收取郵件if get_mail(server, index):server.dele(index)
解析郵件為EmailMessage
解析郵件的時候,可以使用Python 3的email 庫。
我們使用email的parser.Parser類實例,把郵件內容解析成一個message.EmailMessage實例。
如:
def get_mail(server, index):resp, lines, octets = server.retr(index)if resp != b'+OK':raise Exceptioncontent = b'\r\n'.join(lines).decode('utf-8')msg = Parser().parsestr(content)return msg
獲取郵件中的附件
如果我們需要獲取郵件中的附件,可以進一步解析EmailMessage。
解析EmailMessage的時候,需要注意有的郵件文本也是以附件形式放入郵件中的。所以,需要判斷附件的名字是不是message。
正文的文本有可能使用了非ASCII的編碼,可以通過EmailMessage的get_charset
方法取得。如果取得失敗,再通過ContentType域取得。
def guess_charset(msg): charset = msg.get_charset() if charset: return charset content_type = msg.get('Content-Type', '').lower() pos = content_type.find('charset=')if pos >= 0:remain = content_type[pos + 8:].strip() pos = remain.find(';') if pos >= 0: return remain[:pos] return remain return None
最后完整的解析代碼如下:
from email.utils import parseraddr
from email.header import decode_headerdef save_attachment(path, msg):content_type = msg.get_content_type()content = msg.get_payload(decode=True)if content_type == 'text/plain' or content_type == 'text/html':charset = guess_charset(msg)if charset:content = content.decode(charset)else:content = content.decode('utf-8')with open(path) as fp:fp.write(content)def parse_message(msg)# 獲取郵件主題subject = msg.get('Subject', '')# 獲取發件人from_value, from_addr = parseaddr(msg.get('From', ''))# 獲取收件人to_value, to_addr = parseaddr(msg.get('To', ''))# 判斷是否有附件if msg.is_multipart():parts = msg.get_payload()for n, part in enumerate(parts):name = part.get_filename('message')if name == 'message':save_attachment('message', part)else:realname = decode_str(name)attapath = os.path.join(attachments_path, realname)save_attachment(attapath, part)else:save_attachment('message', msg)
使用smtplib發送郵件
發送郵件相對來說比收取簡單,因為格式我們可以控制,只要采用最簡單標準的就行了。
先生成一個EmailMessage。
def generate_message(subject, from, to, message, attchment_list):if len(attchment_list) == 0:message = MIMEText(message, 'plain', 'gb2312')else:message = MIMEMultipart()message.attach(MIMEText(message, 'plain', 'gb2312'))message['Subject'] = Header(subject, 'gb2312')message['From'] = frommessage['To'] = to# attachment_list,是一個列表,每一項是一個包含Content-Type與路徑的字典。for att in attachment_list:add_attachment(message, att['content-type'], att['path'])return message
追加附件的實現如下:
def add_attachment(message, contenttype, path):# Content-Type一般是text/html這種格式appname, subtype = contenttype.split('/')# 取得文件名作為附件里文件的名字,加入附件EmailMessage的Header_dirname, basename = os.path.split(path)encodedname = Header(basename, 'gb2312').encode()att = MIMEBase(appname, subtype, name=encodedname)att.add_header('Content-Disposition', 'attachment', filename=encodedname)# 加入附件內容att.set_payload(open(path, 'rb').read(), 'base64')message.attach(att)
使用smtplib發送的過程,類似與poplib。
- 創建一個SMTP(或者SMTP_SSL)實例
- 連接服務端(connect)
- 登錄服務端(login)
- 發送郵件(sendmail)
import smtplibdef smtp_send(host, port, is_ssl, username, password, sender, receivers, message):if is_ssl:server = smtplib.SMTP_SSL()else:server = smtplib.SMTP()server.connect(host=host, port=port)server.login(username, password)server.sendmail(sender, receivers, message.as_string())