发邮件比较简单。但收邮件却要麻烦一些。这里涉及到邮件的内容、标题、发件人等解析。
下面以QQ邮箱为例。需要注意的是,一些公司exchange邮箱往往需要公司在后台开启授权。
import smtplib
from email.header import Header # 用来对Email标题进行编码
from email.mime.text import MIMEText # 负责构造文本
from email.mime.image import MIMEImage # 负责构造图片
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase # 添加附件的时候用到
from email.utils import parseaddr, formataddr
from email.mime.application import MIMEApplication
## 需要进行pop设置的邮箱有:ailiyun、qq,163等
#设置登录及服务器信息
import poplib
#import settings
import imaplib
import email #导入两个库
import base64
from email.parser import Parser
from email.header import decode_header
from email.utils import parseaddr
class UserInfo:
def __init__(self,mail_address,mail_password):
self.mail_user_name = mail_address.split("@")[0] # 邮件用户名
self.mail_password = mail_password
self.mail_address = mail_address
## 收发邮箱的配置
class MailSettings:
# port => 126,: 25 qq和163: 465 ,ssl: 443,ailiyun:993
def __init__(self,name,send_host,send_port,server_host,server_port,is_ssl_host ):
self.send_port = send_port
self.send_host = send_host
self.server_host = server_host
self.server_port = server_port
self.is_ssl_host= is_ssl_host
self.name = name
def get_attaches():
pass
## 指一次邮件内容,发送多个不同的收件人
## 如果一次邮件发送,只显示一个收件人,如果有N个收件人,就需要定义N个邮件的message
def set_email_from_and_to_info(user,receivers,subject):# subject 是一个说明
#添加一个MIMEmultipart类,处理正文及附件
message = MIMEMultipart()
message['From'] = user.mail_address
message['To'] = ", ".join(receivers) ## 可以针对多个,这个会在这封邮件上显示所有的发送的邮件
message['Subject'] = subject
return message
## 可以处理各类附件
def set_content_and_attaches(message,content_body,content_type,attache_paths):
#正文通常有html和普通文本(plain)格式; html格式比较方便排版和制表
content = MIMEText(content_body, content_type, 'utf-8') ## plain 和 html不同的方式
message.attach(content) ## 正文
## 下面处理相关的附件
attach_num =0
for attach_path in attache_paths:
attach_num += 1
attach_name = get_filename_from_path(attach_path) ## 把路径拆成文件
file_name = "附件_:"+ str(attach_num) + ":" + attach_name
_part = MIMEApplication(open(attach_path,'rb').read()) ## 附件1
_part.add_header('Content-Disposition', 'attachment', filename=file_name)
message.attach(_part)
return message
def get_filename_from_path(path):
split_info = path.split("\\")
return split_info[-1]
def send_email(message,user,mail_info,receivers): ## to be continued
try:
if mail_info.is_ssl_host:
print("smtp_ssl connect -> ")
smtp = smtplib.SMTP_SSL(mail_info.send_host,mail_info.send_port) ##
#smtp.set_debuglevel(2) ## 如果需要打印出调试信息的话
else:
print("smtp connect ->")
smtp = smtplib.SMTP()
smtp.connect(mail_info.send_host,mail_info.send_port)
smtp.ehlo()
smtp.starttls()
#smtp.set_debuglevel(2) ## 如果需要打印出调试信息的话
print("login .....")
smtp.login(user.mail_user_name,user.mail_password)
## 单独发送还是统一发送
print("login 成功! send_mail....")
#print(message)
smtp.sendmail(user.mail_address,receivers,message.as_string()) ##单独发送;不是集合发送; 统一发送是另外模式
print('邮件发送成功!',receivers)
smtp.quit()
except smtplib.SMTPException as e:
print('邮件发送失败,具体信息:',e)
# SMTP 协议用于发送邮件,POP3 和 IMAP 协议用于接收邮件
def pop_receive_email(user,mail_info):
try:
# 连接到POP3服务器:
server = poplib.POP3(mail_info.server_host) #settings.pop3_server)
# 身份认证:
server.user(user.mail_user_name) # mail_address ?
server.pass_(user.mail_password)
# stat()返回邮件数量和占用空间:
print('Messages: %s. Size: %s' % server.stat())
# list()返回所有邮件的编号:
resp, mails, octets = server.list()
# 可以查看返回的列表类似[b'1 82923', b'2 2184', ...]
# 获取最新一封邮件, 注意索引号从1开始:
latest_mail_index = len(mails)
resp, lines, octets = server.retr(latest_mail_index)
# lines存储了邮件的原始文本的每一行,
# 可以获得整个邮件的原始文本:
msg_content = b'\r\n'.join(lines).decode('gbk') ## utf-8
# 稍后解析出邮件:
msg = Parser().parsestr(msg_content)
print(msg)
# 邮件索引号直接从服务器删除邮件
# server.dele(index)
# 关闭连接:
server.quit()
except BaseException as e:
print(e)
def imap_receive_email(user,mail_info):
try:
M = imaplib.IMAP4_SSL(host = mail_info.server_host,port = mail_info.server_port)
print('已连接服务器')
M.login(user.mail_user_name,user.mail_password)
print('已登陆')
print(M.noop())
M.select()
typ, data = M.search(None, 'ALL')
for num in data[0].split():
typ, data = M.fetch(num, '(RFC822)')
# print('Message %s\n%s\n' % (num, data[0][1]))
# print(data[0][1].decode('utf-8'))
msg = email.message_from_string(data[0][1].decode('gbk')) # bug
print(msg)
break
M.close()
M.logout()
except BaseException as e:
print(e)
# 主题解析
def parser_subject(msg):
subject = msg['Subject']
value, charset = decode_header(subject)[0]
if charset:
value = value.decode(charset)
print('邮件主题: {0}'.format(value))
return value
# 邮件信息解析
def parser_address(msg):
hdr, addr = parseaddr(msg['From'])
# name 发送人邮箱名称, addr 发送人邮箱地址
name, charset = decode_header(hdr)[0]
if charset:
name = name.decode(charset)
print('发送人邮箱名称: {0},发送人邮箱地址: {1}'.format(name, addr))
## 如何收特定日期的邮件?
## 邮件的接收是全量推送?
## 邮件的解析,如何落附件?
qq_send_host = 'smtp.qq.com'#
qq_server_host = "imap.qq.com"
qq_send_port = 465 #465
qq_server_port = 993 ## 收邮件
qq_is_ssl_host = True
mail_address = '*****@qq.com'
mail_pass ='n********' ## qq是授权码,不是账户密码
receivers = ['h*****@sina.com.cn',"s****@163.com"]
subject = "标题:这是测试邮件,请不要回复!"
content_body = "正文:这是测试邮件的正文"
content_type = "plain"
#content_html = r'D:\mail\content.html' # 正文html空模版
attach_01 = r"D:\mail\df_y.csv" # xlsx
attach_02 = r'D:\mail\签名.jpg' #jpg
attach_03 = r'D:\mail\上海房地产市场最新报告.pdf' #pdf
attaches = [attach_01,attach_02,attach_03]
user = UserInfo(mail_address,mail_pass)
mail_settings = MailSettings("qq",qq_send_host,qq_send_port,qq_server_host,qq_server_port,qq_is_ssl_host)
mode = 1 # 0 :=>发邮件; 1: 收邮件
if mode ==0:
message = set_email_from_and_to_info(user,receivers,subject)
message = set_content_and_attaches(message,content_body,content_type,attaches)
#print("message :",message)
send_email(message,user,mail_settings,receivers)
else:
if 'imap' in mail_settings.server_host: # imap
imap_receive_email(user,mail_settings)
else: # pop3
pop_receive_email(user,mail_settings)