01
發送郵件
發送郵件使用SMTP協議【Simple Mail Transfer Protocol簡單的郵件傳輸協議】,SMTP協議是SMTP客戶端與SMTP伺服器之間的通信協議。
python中發送郵件使用的模塊有smtplib和email:
1.使用smtplib模塊進行發送郵件;
2.使用email模塊來添加發送的郵件內容。
1. smtplib模塊
導入模塊:import smtplib
1.1. 創建SMTP對象
smtplib.SMTP
和smtplib.SMTP_SSL
:均可以用來創建SMTP對象;
smtplib.SMTP_SSL
:使用安全加密的SSL協議連接到SMTP伺服器;
smtplib.SMTP
:沒有進行安全加密。
故若待測試郵箱不允許使用非SSL和非TLS頻道通信時,則無法使用smtp.SMTP方式來創建客戶端對象。
【查看郵箱的通信方式:郵箱設置菜單中,查看郵箱的接收伺服器和發送伺服器信息。】
如:騰訊企業郵箱
接收伺服器:
imap.exmail.qq.com(使用SSL,埠號993)
發送伺服器:
smtp.exmail.qq.com(使用SSL,埠號465)
-
smtplib.SMTP(host,port,local_hostname,timeout,source_address)
-
smtplib.SMTP_SSL(host, port, local_hostname, keyfile, certfile, timeout, source_address, context)
創建SMTP對象。
host:SMTP發送伺服器主機
port:SMTP伺服器埠號
1.2. SMTP對象操作
-
login(user, password, *, initial_response_ok=True)
SMTP對象登錄
user:授權登錄的用戶名
password:授權登錄的密碼
-
sendmail(from_addr, to_addrs, msg, mail_options=[], rcpt_options=[])
SMTP對象發送郵件
from_addr:發件人地址,字符串類型。
to_addr:收件人地址,包括收件人和抄送人。
多個收件人時to_addr參數為列表,單個收件人時to_addr參數可以為列表或字符串。
msg:要發送的信息
-
quite終止SMTP會話
2. 發送郵件的實例
2.1. 添加郵件內容,包括收件人、抄送人、正文、附件
from email.mime.multipart import MIMEMultipart
from email.header import Header
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import os
class EmailContent:
def __init__(self, senderAdr, emailSubject, toReceivers, ccReceivers):
# 郵件對象
self.msg = MIMEMultipart
# 添加發件人頭
self.msg['From'] = Header("測試" + "<" + senderAdr + ">", 'utf-8')
# 添加收件人
if isinstance(toReceivers, str):
self.msg["To"] = toReceivers
elif isinstance(toReceivers, list):
self.msg['To'] = ";".join(toReceivers)
# 添加抄送人
if isinstance(ccReceivers, str):
self.msg["Cc"] = ccReceivers
elif isinstance(ccReceivers, list):
self.msg["Cc"] = ";".join(ccReceivers)
# 添加郵件主題
self.msg['Subject'] = Header(emailSubject, "utf-8")
def addBody(self, bodyType):
"""
添加不同的郵件正文的實例
1. body為字符串:(如)"這是一個郵件正文內容"
2. body為html格式的字符串:(如)"<div><p>第一段</p><p> 第二段</p></div>"
3. body正文中包含有圖片:
"""
if bodyType == "string":
body = "這是一個郵件正文內容"
mimeText = MIMEText(body, "plain", "utf-8")
self.msg.attach(mimeText)
elif bodyType == "html":
body = "<div><p>第一段</p><p> 第二段</p></div>"
mimeText = MIMEText(body, "html", "utf-8")
self.msg.attach(mimeText)
elif "image" in bodyType:
imageFile = "E://log//test.png"
imageId = os.path.split(imageFile)[1]
# 添加內容
body = '''
<p>測試圖片為:</p>
<p><img src="cid:{imageId}"></p>
'''.format(imageId=imageId)
mimeText = MIMEText(body, "html", "utf-8")
self.msg.attach(mimeText)
# 讀取圖片,並設置圖片id用於郵件正文引用
with open(imageFile, "rb") as fp:
mimeImage = MIMEImage(fp.read)
mimeImage.add_header("Content-ID", imageId)
self.msg.attach(mimeImage)
def addAttachment(self, attachmentName):
"""
添加附件
:return:
"""
file = "E://log//test.txt"
# file = "E://log//test.zip"
# file = "E://log//test.png"
filePath, fileName = os.path.split(file)
print("fileName =", fileName)
enclosure = MIMEText(open(file, 'rb').read, 'base64', 'utf-8')
enclosure['Content-Type'] = 'application/octet-stream'
if attachmentName == "英文":
enclosure['Content-Disposition'] = 'attachment; filename="%s"' % fileName
elif attachmentName == "中文":
enclosure.add_header("Content-Disposition", "attachment", filename=("gbk", "", fileName))
self.msg.attach(enclosure)
2.2. 發送郵件
import smtplib
def SendEmail:
"""發送郵件"""
# SMTP的伺服器信息
smtpHost = "smtp.exmail.qq.com"
sslPort = 465
senderAdr = "xx@xx.cn"
senderPwd = "XXXX"
# 創建SMTP對象
smtpServer = smtplib.SMTP_SSL(smtpHost, sslPort)
# # 設置debug模塊
# smtpServer.set_debuglevel(True)
# 登錄
smtpServer.login(senderAdr, senderPwd)
# 添加郵件內容
toReceivers = ["a@xx.cn", "b@xx.cn"]
ccReceivers = ["d@xx.cn", "e@xx.cn"]
toAddrs = toReceivers + ccReceivers
emailSubject = "這是個編程技術圈自動發送的郵件"
emailContent = EmailContent(senderAdr, emailSubject, toReceivers, ccReceivers)
emailContent.addBody("html")
emailContent.addAttachment("英文")
message = emailContent.msg
# 發送
smtpServer.sendmail(senderAdr, toAddrs, message.as_string)
# 終止SMTP會話
smtpServer.quit
SendEmail
02
讀取郵件
收取郵件使用POP3協議;
解析郵件:需要將收取的郵件轉化為email.message.Message對象,再使用email模塊解析內容。
1. 讀取郵件的實例
1.1. 獲取某封郵件的對象
import poplib
from email.parser import Parser
"""POP的伺服器信息"""
popHost = "pop.exmail.qq.com"
userAdr = "xx@xx.cn"
userPwd = "xxxxx"
""" 創建POP3對象,添加用戶名和密碼"""
pop3Server = poplib.POP3(popHost)
pop3Server.user(userAdr)
pop3Server.pass_(userPwd)
"""獲取郵件數量和占用空間"""
messageCount, mailboxSize = pop3Server.stat
"""獲取郵件請求返回狀態碼、每封郵件的字節大小(b'第幾封郵件 此郵件字節大小')、"""
response, msgNumOctets, octets = pop3Server.list
""" 獲取任意一封郵件的郵件對象【第一封郵件的編號為1,而不是0】"""
msgIndex = random.randint(1,messageCount)
print(msgIndex)
# 獲取第msgIndex封郵件的信息
response, msgLines, octets = pop3Server.retr(msgIndex)
# msgLines中為該郵件的每行數據,先將內容連接成字符串,再轉化為email.message.Message對象
msgLinesToStr = b"\r\n".join(msgLines).decode("utf8", "ignore")
messageObject = Parser.parsestr(msgLinesToStr)
print(messageObject)
""" 終止POP3服務"""
pop3Server.quit
1.2. 解析郵件對象
1.2.1. 獲取郵件日期
msgDate = messageObject["date"]
print(msgDate)
1.2.2. 獲取郵件發件人實名、郵箱地址
獲取郵件實名時,名稱一般是加密的,此時就需要對頭文件進行解碼才可獲取它的實際內容
from email.header import decode_header
def decodeMsgHeader(header):
"""
解碼頭文件
:param header: 需解碼的內容
:return:
"""
value, charset = decode_header(header)[0]
if charset:
value = value.decode(charset)
return value
from email.utils import parseaddr
senderContent = messageObject["From"]
# parseaddr函數返回的是一個元組(realname, emailAddress)
senderRealName, senderAdr = parseaddr(senderContent)
# 將加密的名稱進行解碼
senderRealName = decodeMsgHeader(senderRealName)
print(senderRealName)
print(senderAdr)
1.2.3. 獲取郵件主題
獲取的郵件的主題也是加密的,此時就需要對頭文件進行解碼才可獲取它的實際內容
msgHeader = messageObject["Subject"]
# 對頭文件進行解碼
msgHeader = decodeMsgHeader(msgHeader )
print(msgHeader)
1.2.4. 獲取郵件正文
一封郵件的正文內容,可能是由幾部分構成,每部分的格式不同。
"""獲取郵件正文內容"""
msgBodyContents =
if messageObject.is_multipart: # 判斷郵件是否由多個部分構成
messageParts = messageObject.get_payload # 獲取郵件附載部分
for messagePart in messageParts:
bodyContent = decodeBody(messagePart)
if bodyContent:
msgBodyContents.append(bodyContent)
else:
bodyContent = decodeBody(messageObject)
if bodyContent:
messageBodyContents.append(bodyContent)
print(msgBodyContents)
def decodeBody(msgPart):
"""
解碼內容
:param msgPart: 郵件某部分
"""
contentType = msgPart.get_content_type # 判斷郵件內容的類型,text/html
textContent = ""
if contentType == 'text/plain' or contentType == 'text/html':
content = msgPart.get_payload(decode=True)
charset = msgPart.get_charset
if charset is None:
contentType = msgPart.get('Content-Type', '').lower
position = contentType.find('charset=')
if position >= 0:
charset = contentType[position + 8:].strip
if charset:
textContent = content.decode(charset)
return textContent
1.2.5. 獲取郵件附件
郵件附件名為中文時,需藉助頭文件解碼方式進行解碼,否則會為亂碼。
messageAttachments =
if messageObject.is_multipart: # 判斷郵件是否由多個部分構成
messageParts = messageObject.get_payload # 獲取郵件附載部分
for messagePart in messageParts:
name = messagePart.get_param("name") # 名字存在,則表示此部分為附件
if name:
fileName = decodeMsgHeader(name) # 解碼
messageAttachments.append(fileName)
else:
name = messageObject.get_param("name")
if name:
fileName = decodeMsgHeader(name) # 解碼
messageAttachments.append(fileName)
print(messageAttachments)
2. 讀取郵件時遇到的問題
2.1. 提示「poplib.error_proto: line too long」
File "XXX/EmailInfo.py", line 22, in getMessageObject
return parser.Parser.parsestr(b"\n".join(self.popServer.retr(i)[1]).decode("utf8", "ignore"))
File "/usr/local/lib/python3.6/poplib.py", line 248, in retr
return self._longcmd('RETR %s' % which)
File "/usr/local/lib/python3.6/poplib.py", line 183, in _longcmd
return self._getlongresp
File "/usr/local/lib/python3.6/poplib.py", line 168, in _getlongresp
line, o = self._getline
File "/usr/local/lib/python3.6/poplib.py", line 130, in _getline
raise error_proto('line too long')
poplib.error_proto: line too long
POP3對行長度做了限制,默認為_MAXLINE = 2048,故若是郵件超過此長度就會提示「poplib.error_proto: line too long」。
解決方案:在讀取郵件代碼中重新定義最大行長度,即給poplib._MAXLINE設置新值。
import poplib
poplib._MAXLINE=20480
- end -