smtplib_email邮件、itches微信和twilio短信

在大咖党党支部工作,负责端茶、倒水、查资料、收快递等杂事的小拍“荣幸”滴被抓去当苦力。苦差事是把上个季度没交会费的大咖找出来,挨个儿地通知缴费。这活儿有多吃力不讨好,干过的都~知道!不过领导有所不知的是其实小拍身怀“绝技”(自封冒牌“扫地僧”),这回他就要放“大招”来解决这个问题啦。

小拍闹明白这些密密麻麻的表格是有电子版的(excel),就马上把电子版的excel表格duesRecords.xlsx拿了过来:

仔细阅读表格,发现表格登记的联络方式有电子邮件、手机号和微信昵称(小拍早已利用大咖党支部“勤快小跑腿”的名义使各位大咖……的助理成为他的微信好友),如果写一段代码从excel文件中提取出欠费的名字和对应的电子邮件、手机和微信号,然后一一发送催缴邮件、短信和微信的程序岂不是省时省力?

催缴党费通知的内容如下:
题目:拖欠的人是可耻的!
正文:我说内个{老谁家小谁}啊,{某个季度}的党费你想拖到什么时候啊?赶紧交!明天不把帐结清我卸了你家蟑螂的腿!!!
落款:大咖党党支部

“老谁家小谁”用欠款大咖的姓名取代,“某个季度”用excel表格记录的最后一个季度来替代

这里把发送邮件的函数sendMessage()放进sendMail.py,发送短信的函数sendMessage()放进sendMessage.py,发送微信的函数sendMessage放进sendWechat.py。用时在程序首部import sendMail、import sendMessage和import sendWechat。记住要把存放sendMail.py、sendMessage.py和sendWechat.py的目录放进python解释器的搜索路径内(参见我正在写的《PythonABC基础教程》2.7.3 数据文件和设置搜索路径~^_^),简单说来就是:pycharm菜单项->preferences->自己的项目下的Project Integreter->解释器旁边的设置-> show all -> 点击show paths for the selected interpreter ->  点击+后选择要引入的.py所在的文件夹。否则代码中import XXXXX下面会出现红线,提示找不到XXXXX模块。 

发送微信sendWechat.py的内容如下: 

import itchat

def sendMessag(name, msg):

	itchat.auto_login(True)		# 扫码登录
	friendL = itchat.search_friends(name=name )	
	# 搜索昵称或备注名是name的好友,放进friendL列表
	if friendL:	# 列表不为空,说明搜到了
		itchat.send_msg(msg, friendL[0]['UserName']) 
		# 发送文本信息,这里不严谨,只发给列表里的第一个好友了
	else:
		print(name, '不在你的微信好友列表里')

发送邮件sendMail.py的内容:

import smtplib
from email.mime.text import MIMEText

# 发送文本文件,形参接受邮件地址和邮件正文
def sendMessage(toAddr, body, subject):
    # 登陆邮件服务器
    smtpObj = smtplib.SMTP_SSL('smtp.qq.com', 465)	# 连接邮件服务器
smtpObj.login(This email address is being protected from spambots. You need JavaScript enabled to view it.', 'teywhlqilmuvdfjd')	
# 用自己用的邮件服务器、端口、个人邮箱和授权码替换掉相应参数

    msg = MIMEText(body)	# 生成容器对象
    msg['From'] = This email address is being protected from spambots. You need JavaScript enabled to view it.'	# 发送方
    msg['To'] = toAddr					# 接收方
msg['Subject'] = subject			# 邮件主题

    sendmailStatus = smtpObj.send_message(msg)	# 发送邮件,返回发送状态
    if sendmailStatus != {}:			# 状态不为空说明有错误发生
        print('There was a problem sending email to {}: {}'.format(toAddr, sendmailStatus))

    smtpObj.quit()						# 断开连接

发送短信比较特殊,twilio免费账号只能给注册的手机发送短信,再多就得付费了。国内提供类似服务的公司也有这样的限制,好在相比微信和邮件,短信只是一种辅助。sendMessage.py内容如下:

# 实用twilio免费发送短信的服务发送短信,accountSID and authToken通过在twilio网站上注册获得
from twilio.rest import Client

def sendMessage(num, body):
	accountSID = "AC123456789"
	# 用自己注册时获得的Account Sid替换掉这里的"AC123456789"
	authToken = "987654321
	# 用自己注册时获得的Auto Token替换掉这里的"987654321"
	myTwilioNumber = '+14321234567'	# 用分配给自己的twilio number替换掉'+14321234567'
	myCellPhone = num		# 接收方的手机号码

	client = Client(accountSID, authToken)		# 生成对象client
	client.messages.create(to=myCellPhone, body= body, from_=myTwilioNumber)
	# 指明发送方、接收方和内容,发送短信,生成短信对象message

这里只提取最新季度的欠费,所以用表单对象的max_column属性(sheet.max_column)来取得是否欠费数据。这就有个潜在的问题:比如有数据区最大列是第八列,那么max_column为8。如果再加一列(此时max_column变为9),随后又删掉了数据(不是删除这一列),那么数据删除数据后这个max_column的值仍然为9,而程序是依靠最后一列来判断是否处于欠费状态,这样问题就出现了……有兴趣的朋友对此可以自己进行优化。

从excel文件中提取为缴费的信息,发电邮、短信和微信催缴:

import openpyxl
import sendMail, sendWechat, sendMessage
import time

# 打开excel文件,获取数据:没缴费的姓名、电邮、手机和微信昵称
wb = openpyxl.load_workbook('/usr/PythonABC/documents/duesRecords.xlsx')
sheet = wb.get_sheet_by_name('Sheet1')

lastCol = sheet.max_column							(1)
# excel表格有数据区的最大列
quarter = sheet.cell(row=1, column=lastCol).value
# 第一行是表头,可以获得催交的是哪个季度的党费

# 遍历excel数据行获得缴费数据和联络方式,如果需要缴费就发邮件、微信和短信
for r in range(2, sheet.max_row + 1):	# 从第二列开始是数据
	payment = sheet.cell(row=r, column=lastCol).value	
	# 获得缴费状态
	name = sheet.cell(row=r, column=1).value	# 提取名字

	if name!='' and payment != '已付':	
	# 名字不为空且缴费状态不是"已付"
		email = sheet.cell(row=r, column=2).value	# 提取电邮地址
		mobilNum = sheet.cell(row=r, column=3).value # 提取手机号
		wechatNickname = sheet.cell(row=r, column=4).value
		# 提取微信号
		body = ‘’’
我说内个{}啊,{}的党费你想拖到什么时候啊,赶紧交!明天不把帐结清信不信我卸了你家蟑螂的腿!!!
大咖党党支部 '’’.format(name, quarter)		# 发送的正文

		if email != '':				# email地址不为空
			print('Sending email to {}...'.format(name))
			# 提示正在发送邮件
                        subject = '拖欠的人是可耻的'
			sendMail.sendMessage(email, body, subject )
			# 调用发送邮件模块里的发送文本邮件的函数

		if mobilNum != '':
			print('Sending message to {}...'.format(name))		
			# 提示正在发送短信
			sendMessage.sendMessage(mobilNum, body)						
                        # 调用发送短信模块的发送短信函数

		if wechatNickname != '':
			print('Sending wechat to {}...'.format(name))		
			# 提示正在发送微信
			sendWechat.sendMessag(wechatNickname, body)	
			# 调用发送模块的发送微信函数
		time.sleep(.5)			
		# 防止因一次发送太多被限流或关"禁闭",自己加了个半秒的时延