这里有新鲜出炉的 Python3 官方中文指南,程序狗速度看过来!
Python 是一种面向对象、解释型计算机程序设计语言,由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。Python 语法简洁而清晰,具有丰富和强大的类库。它常被昵称为胶水语言,它能够把用其他语言制作的各种模块(尤其是 C/C++)很轻松地联结在一起。
本文主要介绍了 Python 自动发邮件脚本的相关知识。具有很好的参考价值,下面跟着小编一起来看下吧
缘起
这段时间给朋友搞了个群发邮件的脚本,为了防止进入垃圾邮件,做了很多工作,刚搞完,垃圾邮件进入率 50%,觉得还不错,如果要将垃圾邮件的进入率再调低,估计就要花钱买主机了,想想也就算了,先发一个月,看看效果再拓展吧。
脚本主要是通过 Python 写的,调的 smtplib 库,这些是基础,大家在网上一搜一大堆,今天主要给大家讲解下如何避免进入垃圾邮件系统,以及整个系统搭建时的一些思想。可能刚搞 Python 不久,有很多可能是错误的写法望大家提出来哈~
配置
CentOS7.0 系统
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 的时候可能会遇到你的环境没有安装 gcc 编译环境,执行下面的语句再 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 满状态复活
编译完了之后,将 python3.4 设置为默认 python 解析。
- #ln -s /usr/local/bin/python3.4 /usr/bin/python
链接完成之后检查 python 版本
- #python -V
出现 Python3.4 就标识版本切换完成
系统架构
Account:用于存放发送者邮箱账号的目录,我的 163 邮箱,sina 邮箱,sohu 邮箱和 tom 邮箱均在淘宝上购买了 30 个可以发送 smtp 服务的账号,花费一顿饭的钱不到就可以搞到啦~ 账号密码用【:】分割,每个账号之间使用【,】分割。
Common:引用类文件夹,里面是日至系统配置文件和日志系统源代码
Conf:全局配置文件,目前还木有用到
Image:邮件发送过程中需要使用到的图片资源
Log:日志文件,按日期区分
Logbackups:日志备份文件,用于备份过期日志
Sendmail:用于存储收件人的邮箱信息,账号之间用【,】分割
mail_html.py:主要执行脚本
README.md:git 版本控制用户须知,我是通过码云来管理我的代码的
日志系统
提起脚本系统,日志是相当关键的一个角色,尤其是当你的脚本出错,你要查错的时候,就非常重要了,我也是从网上搞来的一段 Log 日志系统的代码,觉得挺好用,供大伙参考~
主要思想是,打印 log 到指定文件,打印 log 到屏幕,啥也不说了,先上代码,因为是网上的代码,我就放上来啦~
- #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文件中的配置数据#由于config.xml放置在与当前文件相同的目录下,因此通过__file__来获取XML文件的目录,然后再拼接成绝对路径#这里利用了lxml库来解析XML root = ET.parse(os.path.join(os.path.dirname(__file__), 'config.xml')).getroot()#读取日志文件保存路径logpath = root.find('logpath').text#读取日志文件容量,转换为字节logsize = 1024 * 1024 * int(root.find('logsize').text)#读取日志文件保存个数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#日志文件名:由用例脚本的名称,结合日志保存路径,得到日志文件的绝对路径logname = os.path.join(logpath, log_file_name)
- #初始化logger log = logging.getLogger()#日志格式,可以根据需要设置fmt = logging.Formatter('[%(asctime)s][%(filename)s][line:%(lineno)d][%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
- #日志输出到文件,这里用到了上面获取的日志名称,大小,保存个数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)
- #日志接口,用户只需调用这里的接口即可,这里只定位了INFO,
- WARNING,
- ERROR三个级别的日志,可根据需要定义更多接口@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
日志系统的配置文件
- <?xml version="1.0" encoding="utf-8" ?>
- <config>
- <!-- 日志保存路径 -->
- <logpath>
- /Users/litao/Desktop/mail_html/Log
- </logpath>
- <!-- 每个脚本对应的日志文件大小,单位MB -->
- <logsize>
- 8
- </logsize>
- <!-- 每个脚本保存的日志文件个数 -->
- <lognum>
- 100
- </lognum>
- </config>
保存的路径各位随意哈。
如何使用
- logger.info('邮件总数量【' + str(len(recivers)) + '】') logger.info('总计发送邮件数量【' + str(send_num) + '】') logger.info('总计发送错误数量【' + str(error_num) + '】') logger.info('成功邮箱账号集合:' + ','.join(send_success_account)) logger.info('失败邮箱账号集合:' + ','.join(send_failure_account)) logger.info('脚本结束------------------------------------------------------------------') logger.info('')
error 的话将 info 换成 error 即可
执行主文件
提起垃圾邮件,大家首先想到的就是那个令人讨厌的垃圾箱里面的营销邮件,但是,如果你的邮件内容写的很棒,是不是就可以避免被封杀,答案是否定的,邮件被封杀是机器干的,如果是机器干的事,那就好办了,首先,我们得先知道机器的工作原理。
大部分被列为垃圾邮件的邮件均有两个特征:内容不变,IP 不变,其实做到内容一直变,IP 一直变理论上就可以做到不进垃圾邮箱,但是哪有那么多的人力物力做这事,所以,我们要做的是解决概率性的问题。
内容混淆
内容不变我们可以使用多套模板,嵌套着发,这个问题好解决,但是 IP 不变,这个就难一点了,其实我也没解决,主要是怕花钱,我能做的就是通过多套模板来实现内容概率性的不被封杀。
好了,我们先准备 30 个邮件的 subject,30 套邮件的内容模板,下面就是我的全局 subject 配置
这样做的好处就是可以防止邮件的内容被封杀,假设我们 30 秒发一封邮件,那么在 20 分钟内的邮件,没有一封是重复的。我们是从接受邮箱域名的角度考虑的,也就是如果我们的营销对象全是 QQ 邮箱,那么 QQ 邮箱的邮箱服务器在 20 分钟内收到同一 IP 的邮件内容是不一样的,这很大程度上就能避免被封杀。
账户混淆
设置这么多账号是干嘛用的呢,主要还是想混淆机器,让垃圾邮件进率更低。
下面我个人经过测试,发现邮箱服务器具有的一些特性。
163 邮箱
163 邮箱设置了每天每个账号邮件发送的上限位 50 封,账号 554 出错重发的时间是 3 小时。
tom 邮箱
tom 邮箱每天邮件发送数量不做限制,我们也假设是 50 封,但是每封邮件之间的发送间隔一定要超过 30 秒,要不然会被短时间连接数过大报错。
sohu 邮箱
业界良心,基本上没出过啥错误,一直保持着良好的发送成功率。我们也将其定位发送间隔 30 秒,每日上线 50 封。
sina 邮箱
恶心的玩意儿,每次发送邮箱前需要先登录,认证手机号,每个手机号 5 个邮箱哈,但是效果显著,认证完毕,和 sohu 一样,基本没出错过。
时间混淆
有了这些基础,我们就可以知道了,我们有 120 个账号,30 个邮件模板,每天一刻不停的发送,每封邮件之间的间隔为 30 秒,一天的邮件发送量在 2800 封左右。
我觉得一天 2800 封,如果有钱的话,一台 ESC 的费用是 3 元每天,独立 ip 哈,如果找第三方发送,一封邮件是 3 分钱,量大 2 分钱,他们是 EDM 的,我测试过 1500 封,达到率不足千分之一。也就是说,我们发送 1500 封,只需要 1 块多钱,找第三方发送,1500 封怎么也得 40 块钱。成本是不是很低。
好的,那就来看看邮件是如何发送的吧。
邮件发送
下面我们来看下我的主文件是如何搞的
- #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 *
导入模块,以来的外部库和内部的文件
#目录主位置
_root_dir = '/Users/litao/Desktop/mail_html/'
_title_common = '愚人节'
愚人节主题礼物,也是为了以后省事,subject 和内容中设计到 title 的均会被改为愚人节,马上愚人节了吗,营销方式,代码实现,异常方便修改。
- #邮箱内容设置_content = """\
- <html>
- <style> .title{font-weight:bold;font-size:18px;}</style>
- <body>
- <p>
- <img src="cid: image1 ">
- <br><br>
- <span
- class="title ">【愚人节】将至,您还没准备礼物?那你一定会过个开心的愚人节的</span>
- <br>
- 愚人节就要来啦,礼朵朵给大伙准备了大批量的礼物伴你度过愚人节,具体百度一下【礼朵朵】,赶紧进站选礼物吧~
- <br>
- 选礼物前别忘了先去心愿墙许愿哟,你的愿望可能被礼朵朵看到,可以帮你实现哟,实现的时候别忘了来礼朵朵还愿哈~
- <br><br>
- <span class="title ">【礼朵朵】介绍</span>
- <br>
- 国人从古至今都有送礼的习俗,送礼作为传统之一,一直流传至今,礼尚往来成为人生必修课。
- <br>
- 【礼朵朵】集合商业送礼和现代送礼搭建礼物导购分享平台【朵朵礼物】,带给老少皆宜的送礼分享体验新体验。
- <br>
- 与此同时,礼朵朵还给大伙准备了礼物攻略【礼物说】,让大家可以对礼物有个更全面的了解~
- <br><br>
- <span class="title ">百度搜索【礼朵朵】,开启你的礼物新旅程吧~</span>
- <br><br>
- </p>
- </body>
- </html>
- """
营销内容模板,html 模式实现邮件的发送,少不了有模板~
- #发送邮箱smtp地址_smtp_address = ['smtp.163.com', 'smtp.sina.cn', 'smtp.tom.com', 'smtp.sohu.com']
smtp 地址数组,用于在不同的邮件服务器间切换。
- 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)#构造图片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 = "愚人节活动海报.jpg") msg.attach(msgImage2) msg['From'] = sender msg['To'] = reciver
- #发送邮件smtp = smtplib.SMTP() smtp.connect(smtpadd) smtp.login(username, password) smtp.sendmail(sender, reciver, msg.as_string()) smtp.quit()
发邮件方法,里面有两个地方需要注意,一个是
- msgImage.add_header("Content-ID", "<image1>") msg.attach(msgImage)
将邮件模板中的 image1 的 img 标签内容替换成我们想要的图片
第二个
- fp2 = open(_root_dir + 'Image/yurenjie.png', 'rb') msgImage2 = MIMEImage(fp2.read()) fp2.close() msgImage2.add_header('Content-Disposition', 'attachment', filename = "愚人节活动海报.jpg")
插入附件,图片是一个海报,说起海报,强烈建议大家使用创客贴这个平台,非常好用。
下面就是发送邮件啦!!!
- #发送邮件smtp = smtplib.SMTP() smtp.connect(smtpadd) smtp.login(username, password) smtp.sendmail(sender, reciver, msg.as_string()) smtp.quit()
通用方法,将文件中的以, 分割的内容以数组形式返回
- #读取文件中的数据,并将使用,
- 分割的数据变为数组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、每发送一封邮件,休息 25 秒,切换账号,继续发送
4、日志记录
5、错误处理
- if __name__ == "__main__": content = _content#接收人的邮箱按照每天2000封来,每天的邮箱都需要更换,文件名最后以日期为准,邮件发送量以日志为准recivers = readFileToSplit(_root_dir + 'Sendmail/mail_test.txt')#把4个邮箱的账号都获取到,方便下面
- for循环中使用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')
- #获取邮件发送模板#注意模板之间的切换
- #log_file_stream = open(_root_dir + 'log', 'w+') logger.info('') logger.info('脚本开始------------------------------------------------------------------')
- #统计邮件发送量send_num = 0#统计发送出错量error_num = 0#统计发送失败的邮箱发送账号send_success_account = []#统计发送成功的邮箱发送账号send_failure_account = []
- subject_num = len(_subject)
- #最后统计没有发出去的邮箱号,放到下日,继续发送
- 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('发送账号', sender, '正在发送') str_success_1 = '发送账号【' + sender + '】正在发送'logger.info(str_success_1)#writeLog(log_file_stream, str_success_1)#print('接收序号', i, recivers[i], '发送成功') str_success_2 = '接受序号【' + str(i) + '】【' + recivers[sendindex] + '】发送成功'#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], ',发送失败') str_failure_1 = '产生错误于:【' + sender + '】发送失败'#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, '脚本结束') set(send_success_account) set(send_failure_account) logger.info('邮件总数量【' + str(len(recivers)) + '】') logger.info('总计发送邮件数量【' + str(send_num) + '】') logger.info('总计发送错误数量【' + str(error_num) + '】') logger.info('成功邮箱账号集合:' + ','.join(send_success_account)) logger.info('失败邮箱账号集合:' + ','.join(send_failure_account)) logger.info('脚本结束------------------------------------------------------------------') logger.info('')#log_file_stream.close()
代码就这么多,至于 subject 邮件主题和模板怎么搞,可以自由发挥哈,可以放在主执行文件中,也可以放到配置文件中,实现可以配置,这里就不再赘述啦
开工
下面就可以开工啦,直接到项目主目录
- #python mail_html.py
看到屏幕上有输出就 OK 啦,下面就是等待收获的季节
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持 PHPERZ!
来源: http://www.phperz.com/article/17/0720/332904.html