Python自动发邮件脚本


Posted in Python onMarch 31, 2017

缘起

这段时间给朋友搞了个群发邮件的脚本,为了防止进入垃圾邮件,做了很多工作,刚搞完,垃圾邮件进入率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就标识版本切换完成

系统架构

 Python自动发邮件脚本

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配置

 Python自动发邮件脚本

这样做的好处就是可以防止邮件的内容被封杀,假设我们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啦,下面就是等待收获的季节

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Python 相关文章推荐
python计算程序开始到程序结束的运行时间和程序运行的CPU时间
Nov 28 Python
Python中的对象,方法,类,实例,函数用法分析
Jan 15 Python
对于Python装饰器使用的一些建议
Jun 03 Python
Python可变参数函数用法实例
Jul 07 Python
用不到50行的Python代码构建最小的区块链
Nov 16 Python
Django的HttpRequest和HttpResponse对象详解
Jan 26 Python
python中将一个全部为int的list 转化为str的list方法
Apr 09 Python
使用pytorch进行图像的顺序读取方法
Jul 27 Python
对python:循环定义多个变量的实例详解
Jan 20 Python
python scrapy爬虫代码及填坑
Aug 12 Python
详解Python time库的使用
Oct 10 Python
Python操作PostgreSql数据库的方法(基本的增删改查)
Dec 29 Python
Python中查看文件名和文件路径
Mar 31 #Python
使用python遍历指定城市的一周气温
Mar 31 #Python
python网络编程调用recv函数完整接收数据的三种方法
Mar 31 #Python
Python爬取网易云音乐热门评论
Mar 31 #Python
Python中Django发送带图片和附件的邮件
Mar 31 #Python
使用Python对Access读写操作
Mar 30 #Python
使用Python对Excel进行读写操作
Mar 30 #Python
You might like
全国FM电台频率大全 - 17 湖北省
2020/03/11 无线电
php的字符串用法小结
2010/06/08 PHP
PHP实现的mongoDB数据库操作类完整实例
2018/04/10 PHP
PHP+ajax实现获取新闻数据简单示例
2018/05/08 PHP
Dojo 学习笔记入门篇 First Dojo Example
2009/11/15 Javascript
jQuery1.6 正式版发布并提供下载
2011/05/05 Javascript
Js四则运算函数代码
2012/07/21 Javascript
Javascript级联下拉菜单以及AJAX数据验证核心代码
2013/05/10 Javascript
jquery实现div拖拽宽度示例代码
2013/07/31 Javascript
nodejs调用cmd命令实现复制目录
2015/05/04 NodeJs
jquery图片滚动放大代码分享(2)
2015/08/28 Javascript
jQuery计算文本框字数及限制文本框字数的方法
2016/03/01 Javascript
论Bootstrap3和Foundation5网格系统的异同
2016/05/16 Javascript
vue.js实现含搜索的多种复选框(附源码)
2017/03/23 Javascript
laydate 显示结束时间不小于开始时间的实例
2017/08/11 Javascript
基于vue循环列表时点击跳转页面的方法
2018/08/31 Javascript
jQuery常见的遍历DOM操作详解
2018/09/05 jQuery
新手快速上手webpack4打包工具的使用详解
2019/01/28 Javascript
vue指令做滚动加载和监听等
2019/05/26 Javascript
在antd4.0中Form使用initialValue操作
2020/11/02 Javascript
python中循环语句while用法实例
2015/05/16 Python
python实现批量监控网站
2016/09/09 Python
python 判断字符串中是否含有汉字或非汉字的实例
2019/07/15 Python
FFrpc python客户端lib使用解析
2019/08/24 Python
Pytorch之view及view_as使用详解
2019/12/31 Python
浅谈Python中threading join和setDaemon用法及区别说明
2020/05/02 Python
python爬虫可以爬什么
2020/06/16 Python
资深地理教师自我评价
2013/09/21 职场文书
个人委托书怎么写
2014/04/04 职场文书
监督检查工作方案
2014/05/28 职场文书
中国梦读书活动总结
2014/07/10 职场文书
教师四风问题整改措施
2014/09/25 职场文书
2014县政府领导班子三严三实对照检查材料思想汇报
2014/09/26 职场文书
教师群众路线教育实践活动个人对照检查材料
2014/11/04 职场文书
入党转正申请报告
2015/05/15 职场文书
Pandas-DataFrame知识点汇总
2022/03/16 Python