Python自動發(fā)郵件腳本
緣起
這段時間給朋友搞了個群發(fā)郵件的腳本,為了防止進入垃圾郵件,做了很多工作,剛搞完,垃圾郵件進入率50%,覺得還不錯,如果要將垃圾郵件的進入率再調(diào)低,估計就要花錢買主機了,想想也就算了,先發(fā)一個月,看看效果再拓展吧。
腳本主要是通過Python寫的,調(diào)的smtplib庫,這些是基礎(chǔ),大家在網(wǎng)上一搜一大堆,今天主要給大家講解下如何避免進入垃圾郵件系統(tǒng),以及整個系統(tǒng)搭建時的一些思想??赡軇偢鉖ython不久,有很多可能是錯誤的寫法望大家提出來哈~
配置
CentOS7.0系統(tǒng)
Python 3.4
CentOS7.0下面默認的是Python2.7.5,我們先來將Python的版本提升上去
#wget https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tgz
下載Python3.4版本
#tar -xf Python-3.4.3.tgz #cd Python-3.4.3/ #./configure
這邊configure的時候可能會遇到你的環(huán)境沒有安裝gcc編譯環(huán)境,執(zhí)行下面的語句再configure即可
#yum -y install gcc #yum -y install gdb #yum -y install gcc-c++
編譯安裝
#make #make install
因為替換了python版本之后yum可能不能正常使用,需改兩個文件
#vim /usr/bin/yum #vim /usr/libexec/urlgrabber-ext-down
將這兩個文件的頭部的#!/usr/bin/python改成#!/usr/bin/python2.7即可,保存退出,yum滿狀態(tài)復活
編譯完了之后,將python3.4設置為默認python解析。
#ln -s /usr/local/bin/python3.4 /usr/bin/python
鏈接完成之后檢查python版本
#python -V
出現(xiàn)Python3.4就標識版本切換完成
系統(tǒng)架構(gòu)

Account:用于存放發(fā)送者郵箱賬號的目錄,我的163郵箱,sina郵箱,sohu郵箱和tom郵箱均在淘寶上購買了30個可以發(fā)送smtp服務的賬號,花費一頓飯的錢不到就可以搞到啦~賬號密碼用【:】分割,每個賬號之間使用【,】分割。
Common:引用類文件夾,里面是日至系統(tǒng)配置文件和日志系統(tǒng)源代碼
Conf:全局配置文件,目前還木有用到
Image:郵件發(fā)送過程中需要使用到的圖片資源
Log:日志文件,按日期區(qū)分
Logbackups:日志備份文件,用于備份過期日志
Sendmail:用于存儲收件人的郵箱信息,賬號之間用【,】分割
mail_html.py:主要執(zhí)行腳本
README.md:git版本控制用戶須知,我是通過碼云來管理我的代碼的
日志系統(tǒng)
提起腳本系統(tǒng),日志是相當關(guān)鍵的一個角色,尤其是當你的腳本出錯,你要查錯的時候,就非常重要了,我也是從網(wǎng)上搞來的一段Log日志系統(tǒng)的代碼,覺得挺好用,供大伙參考~
主要思想是,打印log到指定文件,打印log到屏幕,啥也不說了,先上代碼,因為是網(wǎng)上的代碼,我就放上來啦~
# coding: utf-8
#from lxml import etree
import logging.handlers
import logging
import os
import sys
import time
import datetime
try:
import xml.etree.cElementTree as ET
except ImportError:
import xml.etree.ElementTree as ET
# 提供日志功能
class logger:
# 先讀取XML文件中的配置數(shù)據(jù)
# 由于config.xml放置在與當前文件相同的目錄下,因此通過 __file__ 來獲取XML文件的目錄,然后再拼接成絕對路徑
# 這里利用了lxml庫來解析XML
root = ET.parse(os.path.join(os.path.dirname(__file__), 'config.xml')).getroot()
# 讀取日志文件保存路徑
logpath = root.find('logpath').text
# 讀取日志文件容量,轉(zhuǎn)換為字節(jié)
logsize = 1024*1024*int(root.find('logsize').text)
# 讀取日志文件保存?zhèn)€數(shù)
lognum = int(root.find('lognum').text)
# 添加分天日志名
now = datetime.datetime.now()
now_time = now.strftime('%Y%m%d')
log_file_name = sys.argv[0].split('/')[-1].split('.')[0] + '_' + now_time
# 日志文件名:由用例腳本的名稱,結(jié)合日志保存路徑,得到日志文件的絕對路徑
logname = os.path.join(logpath, log_file_name)
# 初始化logger
log = logging.getLogger()
# 日志格式,可以根據(jù)需要設置
fmt = logging.Formatter('[%(asctime)s][%(filename)s][line:%(lineno)d][%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
# 日志輸出到文件,這里用到了上面獲取的日志名稱,大小,保存?zhèn)€數(shù)
handle1 = logging.handlers.RotatingFileHandler(logname, maxBytes=logsize, backupCount=lognum)
handle1.setFormatter(fmt)
# 同時輸出到屏幕,便于實施觀察
handle2 = logging.StreamHandler(stream=sys.stdout)
handle2.setFormatter(fmt)
log.addHandler(handle1)
log.addHandler(handle2)
# 設置日志基本,這里設置為INFO,表示只有INFO級別及以上的會打印
log.setLevel(logging.INFO)
# 日志接口,用戶只需調(diào)用這里的接口即可,這里只定位了INFO, WARNING, ERROR三個級別的日志,可根據(jù)需要定義更多接口
@classmethod
def info(cls, msg):
cls.log.info(msg)
return
@classmethod
def warning(cls, msg):
cls.log.warning(msg)
return
@classmethod
def error(cls, msg):
cls.log.error(msg)
return
日志系統(tǒng)的配置文件
<?xml version="1.0" encoding="utf-8"?> <config> <!-- 日志保存路徑 --> <logpath>/Users/litao/Desktop/mail_html/Log</logpath> <!-- 每個腳本對應的日志文件大小,單位MB --> <logsize>8</logsize> <!-- 每個腳本保存的日志文件個數(shù) --> <lognum>100</lognum> </config>
保存的路徑各位隨意哈。
如何使用
logger.info('郵件總數(shù)量【'+str(len(recivers))+'】')
logger.info('總計發(fā)送郵件數(shù)量【'+str(send_num)+'】')
logger.info('總計發(fā)送錯誤數(shù)量【'+str(error_num)+'】')
logger.info('成功郵箱賬號集合:'+','.join(send_success_account))
logger.info('失敗郵箱賬號集合:'+','.join(send_failure_account))
logger.info('腳本結(jié)束------------------------------------------------------------------')
logger.info('')
error的話將info換成error即可
執(zhí)行主文件
提起垃圾郵件,大家首先想到的就是那個令人討厭的垃圾箱里面的營銷郵件,但是,如果你的郵件內(nèi)容寫的很棒,是不是就可以避免被封殺,答案是否定的,郵件被封殺是機器干的,如果是機器干的事,那就好辦了,首先,我們得先知道機器的工作原理。
大部分被列為垃圾郵件的郵件均有兩個特征:內(nèi)容不變,IP不變,其實做到內(nèi)容一直變,IP一直變理論上就可以做到不進垃圾郵箱,但是哪有那么多的人力物力做這事,所以,我們要做的是解決概率性的問題。
內(nèi)容混淆
內(nèi)容不變我們可以使用多套模板,嵌套著發(fā),這個問題好解決,但是IP不變,這個就難一點了,其實我也沒解決,主要是怕花錢,我能做的就是通過多套模板來實現(xiàn)內(nèi)容概率性的不被封殺。
好了,我們先準備30個郵件的subject,30套郵件的內(nèi)容模板,下面就是我的全局subject配置

這樣做的好處就是可以防止郵件的內(nèi)容被封殺,假設我們30秒發(fā)一封郵件,那么在20分鐘內(nèi)的郵件,沒有一封是重復的。我們是從接受郵箱域名的角度考慮的,也就是如果我們的營銷對象全是QQ郵箱,那么QQ郵箱的郵箱服務器在20分鐘內(nèi)收到同一IP的郵件內(nèi)容是不一樣的,這很大程度上就能避免被封殺。
賬戶混淆
設置這么多賬號是干嘛用的呢,主要還是想混淆機器,讓垃圾郵件進率更低。
下面我個人經(jīng)過測試,發(fā)現(xiàn)郵箱服務器具有的一些特性。
163郵箱
163郵箱設置了每天每個賬號郵件發(fā)送的上限位50封,賬號554出錯重發(fā)的時間是3小時。
tom郵箱
tom郵箱每天郵件發(fā)送數(shù)量不做限制,我們也假設是50封,但是每封郵件之間的發(fā)送間隔一定要超過30秒,要不然會被短時間連接數(shù)過大報錯。
sohu郵箱
業(yè)界良心,基本上沒出過啥錯誤,一直保持著良好的發(fā)送成功率。我們也將其定位發(fā)送間隔30秒,每日上線50封。
sina郵箱
惡心的玩意兒,每次發(fā)送郵箱前需要先登錄,認證手機號,每個手機號5個郵箱哈,但是效果顯著,認證完畢,和sohu一樣,基本沒出錯過。
時間混淆
有了這些基礎(chǔ),我們就可以知道了,我們有120個賬號,30個郵件模板,每天一刻不停的發(fā)送,每封郵件之間的間隔為30秒,一天的郵件發(fā)送量在2800封左右。
我覺得一天2800封,如果有錢的話,一臺ESC的費用是3元每天,獨立ip哈,如果找第三方發(fā)送,一封郵件是3分錢,量大2分錢,他們是EDM的,我測試過1500封,達到率不足千分之一。也就是說,我們發(fā)送1500封,只需要1塊多錢,找第三方發(fā)送,1500封怎么也得40塊錢。成本是不是很低。
好的,那就來看看郵件是如何發(fā)送的吧。
郵件發(fā)送
下面我們來看下我的主文件是如何搞的
#coding=utf-8 import smtplib import mimetypes import time import datetime from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.image import MIMEImage #引入外部文件 from Common.log import *
導入模塊,以來的外部庫和內(nèi)部的文件
#目錄主位置
_root_dir = '/Users/litao/Desktop/mail_html/'
_title_common = '愚人節(jié)'
愚人節(jié)主題禮物,也是為了以后省事,subject和內(nèi)容中設計到title的均會被改為愚人節(jié),馬上愚人節(jié)了嗎,營銷方式,代碼實現(xiàn),異常方便修改。
#郵箱內(nèi)容設置
_content = """\
<html>
<style> .title{font-weight:bold;font-size:18px;}</style>
<body>
<p>
<img src="cid:image1">
<br><br>
<span
class="title">【愚人節(jié)】將至,您還沒準備禮物?那你一定會過個開心的愚人節(jié)的</span>
<br>
愚人節(jié)就要來啦,禮朵朵給大伙準備了大批量的禮物伴你度過愚人節(jié),具體百度一下【禮朵朵】,趕緊進站選禮物吧~
<br>
選禮物前別忘了先去心愿墻許愿喲,你的愿望可能被禮朵朵看到,可以幫你實現(xiàn)喲,實現(xiàn)的時候別忘了來禮朵朵還愿哈~
<br><br>
<span class="title">【禮朵朵】介紹</span>
<br>
國人從古至今都有送禮的習俗,送禮作為傳統(tǒng)之一,一直流傳至今,禮尚往來成為人生必修課。
<br>
【禮朵朵】集合商業(yè)送禮和現(xiàn)代送禮搭建禮物導購分享平臺【朵朵禮物】,帶給老少皆宜的送禮分享體驗新體驗。
<br>
與此同時,禮朵朵還給大伙準備了禮物攻略【禮物說】,讓大家可以對禮物有個更全面的了解~
<br><br>
<span class="title">百度搜索【禮朵朵】,開啟你的禮物新旅程吧~</span>
<br><br>
</p>
</body>
</html>
"""
營銷內(nèi)容模板,html模式實現(xiàn)郵件的發(fā)送,少不了有模板~
#發(fā)送郵箱smtp地址 _smtp_address = ['smtp.163.com','smtp.sina.cn','smtp.tom.com','smtp.sohu.com']
smtp地址數(shù)組,用于在不同的郵件服務器間切換。
def sendMail(sender,reciver,subject,content,passwd,smtpadd):
username=sender
password=passwd
msg=MIMEMultipart('related')
msg['Subject']=subject
#html格式
html=content
htm = MIMEText(html,'html','utf-8')
msg.attach(htm)
#構(gòu)造圖片
fp=open(_root_dir+'Image/logo_small.png','rb')
msgImage=MIMEImage(fp.read())
fp.close()
msgImage.add_header("Content-ID", "<image1>")
msg.attach(msgImage)
fp2=open(_root_dir+'Image/yurenjie.png','rb')
msgImage2=MIMEImage(fp2.read())
fp2.close()
msgImage2.add_header('Content-Disposition', 'attachment',
filename="愚人節(jié)活動海報.jpg")
msg.attach(msgImage2)
msg['From']=sender
msg['To']=reciver
#發(fā)送郵件
smtp=smtplib.SMTP()
smtp.connect(smtpadd)
smtp.login(username, password)
smtp.sendmail(sender, reciver, msg.as_string())
smtp.quit()
發(fā)郵件方法,里面有兩個地方需要注意,一個是
msgImage.add_header("Content-ID", "<image1>")
msg.attach(msgImage)
將郵件模板中的image1的img標簽內(nèi)容替換成我們想要的圖片
第二個
fp2=open(_root_dir+'Image/yurenjie.png','rb')
msgImage2=MIMEImage(fp2.read())
fp2.close()
msgImage2.add_header('Content-Disposition', 'attachment',
filename="愚人節(jié)活動海報.jpg")
插入附件,圖片是一個海報,說起海報,強烈建議大家使用創(chuàng)客貼這個平臺,非常好用。
下面就是發(fā)送郵件啦?。?!
#發(fā)送郵件 smtp=smtplib.SMTP() smtp.connect(smtpadd) smtp.login(username, password) smtp.sendmail(sender, reciver, msg.as_string()) smtp.quit()
通用方法,將文件中的以,分割的內(nèi)容以數(shù)組形式返回
#讀取文件中的數(shù)據(jù),并將使用,分割的數(shù)據(jù)變?yōu)閿?shù)組
def readFileToSplit(filepath):
file_stream = open(filepath)
try:
data = file_stream.read()
finally:
file_stream.close()
data_split = data.split(',')
return data_split
主方法
1、切割賬號
2、切換郵件服務器
3、每發(fā)送一封郵件,休息25秒,切換賬號,繼續(xù)發(fā)送
4、日志記錄
5、錯誤處理
if __name__=="__main__":
content=_content
# 接收人的郵箱按照每天2000封來,每天的郵箱都需要更換,文件名最后以日期為準,郵件發(fā)送量以日志為準
recivers=readFileToSplit(_root_dir+'Sendmail/mail_test.txt')
# 把4個郵箱的賬號都獲取到,方便下面for循環(huán)中使用
account_163=readFileToSplit(_root_dir+'Account/account163')
account_sina=readFileToSplit(_root_dir+'Account/accountsina')
account_tom = readFileToSplit(_root_dir+'Account/accounttom')
account_sohu = readFileToSplit(_root_dir+'Account/accountsohu')
# 獲取郵件發(fā)送模板
# 注意模板之間的切換
#log_file_stream = open(_root_dir+'log', 'w+')
logger.info('')
logger.info('腳本開始------------------------------------------------------------------')
# 統(tǒng)計郵件發(fā)送量
send_num = 0
# 統(tǒng)計發(fā)送出錯量
error_num = 0
# 統(tǒng)計發(fā)送失敗的郵箱發(fā)送賬號
send_success_account = []
# 統(tǒng)計發(fā)送成功的郵箱發(fā)送賬號
send_failure_account = []
subject_num = len(_subject)
# 最后統(tǒng)計沒有發(fā)出去的郵箱號,放到下日,繼續(xù)發(fā)送
for i in range(0, len(recivers)):
try:
sendindex = i - error_num
num = i % 30
account = account_163[num].split(':')
addindex=i%4
subjectindex = sendindex%subject_num
if addindex == 1:
account=account_sina[num].split(':')
elif addindex == 2:
account=account_tom[num].split(':')
elif addindex == 3:
account=account_sohu[num].split(':')
sender=account[0]
passwd=account[1]
smtpadd = _smtp_address[addindex]
#smtpstr=str('163')
sendMail(sender, recivers[sendindex], _subject[subjectindex], content, passwd, smtpadd)
#print('發(fā)送賬號', sender, '正在發(fā)送')
str_success_1 = '發(fā)送賬號【'+sender+'】正在發(fā)送'
logger.info(str_success_1)
#writeLog(log_file_stream,str_success_1)
#print('接收序號', i, recivers[i],'發(fā)送成功')
str_success_2 = '接受序號【'+str(i)+'】【'+recivers[sendindex]+'】發(fā)送成功'
#writeLog(log_file_stream,str_success_2)
logger.info(str_success_2)
logger.info('')
#print('')
send_num+=1
send_success_account.append(sender)
time.sleep(25)
except Exception as e:
#print('停止于:', i, recivers[i],',發(fā)送失敗')
str_failure_1 = '產(chǎn)生錯誤于:【'+sender+'】發(fā)送失敗'
#writeLog(log_file_stream,str_failure_1)
logger.error(str_failure_1)
#print(e)
str_failure_2 = str(e)
#writeLog(log_file_stream,str_failure_2)
logger.error(str_failure_2)
logger.info('')
error_num+=1
send_failure_account.append(sender)
#print('')
#break
#print('安全抵達底部')
#writeLog(log_file_stream,'腳本結(jié)束')
set(send_success_account)
set(send_failure_account)
logger.info('郵件總數(shù)量【'+str(len(recivers))+'】')
logger.info('總計發(fā)送郵件數(shù)量【'+str(send_num)+'】')
logger.info('總計發(fā)送錯誤數(shù)量【'+str(error_num)+'】')
logger.info('成功郵箱賬號集合:'+','.join(send_success_account))
logger.info('失敗郵箱賬號集合:'+','.join(send_failure_account))
logger.info('腳本結(jié)束------------------------------------------------------------------')
logger.info('')
#log_file_stream.close()
代碼就這么多,至于subject郵件主題和模板怎么搞,可以自由發(fā)揮哈,可以放在主執(zhí)行文件中,也可以放到配置文件中,實現(xiàn)可以配置,這里就不再贅述啦
開工
下面就可以開工啦,直接到項目主目錄
#python mail_html.py
看到屏幕上有輸出就OK啦,下面就是等待收獲的季節(jié)
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
相關(guān)文章
Python數(shù)據(jù)可視化中的時間序列圖表功能(實例展示其強大功能)
時間序列圖表在多個領(lǐng)域中都有廣泛的應用,通過Python中的各種繪圖庫和數(shù)據(jù)分析工具,我們可以方便地對時間序列數(shù)據(jù)進行可視化和分析,本文提供的示例代碼和方法能夠為您的時間序列數(shù)據(jù)分析工作提供有益的參考,感興趣的朋友跟隨小編一起看看吧2024-07-07
解決Python 使用h5py加載文件,看不到keys()的問題
今天小編就為大家分享一篇解決Python 使用h5py加載文件,看不到keys()的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-02-02
python的pandas工具包,保存.csv文件時不要表頭的實例
今天小編小編就為大家分享一篇python的pandas工具包,保存.csv文件時不要表頭的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06
python求加權(quán)平均值的實例(附純python寫法)
今天小編就為大家分享一篇python求加權(quán)平均值的實例(附純python寫法),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08
pip安裝python庫時報Failed?building?wheel?for?xxx錯誤的解決方法
最近在使用pip安裝python的時候遇到些問題,所以下面這篇文章主要給大家介紹了關(guān)于pip安裝python庫時報Failed?building?wheel?for?xxx錯誤的解決方法,需要的朋友可以參考下2023-01-01

