作为一个卑微的学生干部,经常会遇到学校安排任务但是同学不积极完成的情况,我们以往的交流,都是基于QQ微信的,但是从我个人来看,QQ设置班级群消息仅接受不提醒是常态,我知道的另一类人是所有群都提醒,消息永远是99+,所以在群里发消息提醒某个别同学去完成任务,被提醒的同学不见得看不看得见,对其他同学也会有打扰。
想起来腾讯云赠送给我的200条免费短信,不禁陷入思考——可以爬一下学校那个网站的数据,然后筛选未完成同学名单,保存到json中,然后调用腾讯云短信的api去发信。
理论成立,实践开始。
短信资格申请
首先,你要有腾讯云账号,并且有个自己的服务器,因为现在腾讯云审核还是比较严格的,发送短信的内容不是头脑一热就可以的发的,必须有签名,有模板,并且都是需要平台审核才能使用的,想我最开始不知道这些,光是申请,一会用腾讯云,一会用阿里云的,申请都从第一天晚上十点搞到了第二天下午。
这里为啥说他审核严格呢,因为申请的时候你必须是要挂靠到某个网站的,就比如我的网站是test.com,那我必须要有test.com这个网站的域名备案后台截图上传作为佐证。签名还得是“test个人站”、“test体温上报提醒”这种名字。审核的人还会去访问你的网站,看和你描述的相不相符,我就是没办法了,直接在服务器上搞了个wordpress论坛,发了几条疫情最新消息的新闻,希望审核放我过。
这两个红框框是需要点进去申请的。
这是申请签名
这是申请正文模板
然后我们就可以去找一下调用的api,我用的是python的
就这玩意,api的文档和调用api的方法清清楚楚。
红框框里这一堆直接复制到python中用就行了,有几个参数直接改就行了。
爬虫部分
由于是登录的,所以用session,这里的接口和参数都是经过修改的,抹除了原有的特征,每个学校大同小异,自己找一下相关接口就可以了。
这一部分代码的思路就是,先session带着登录时的data 以post请求了登录的url,然后获取一个cookie,这个cookie是不需要我们去考虑的,session会自动保存并且在后续的请求中加上这个cookie。
完成登录操作后,用get方式访问接口,获取目标数据,这里的返回值是json的,我直接把他保存在了stu.json中
today = datetime.date.today()
def get_json():
# login_url = 'https://xxxxx.edu.cn/AbnormalList?dateStr={}'.format(today)
login_url = 'https://login.xxx.edu.cn/index.php?rid=verifyUser'
headers = {
# 没有会被直接拦截
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36"
}
data = {
"_eventId": "submit",
"locationurl": "https://xxx.edu.cn/AbnormalList?dateStr=2021-08-03",
"lt": "${loginTicket}",
#这里是你的用户名和密码
"password": "",
"username": ""
}
se = requests.Session()
se.post(login_url, headers=headers, data=data)
print(requests.status_codes)
# 这是一个接口
detail_url = 'https://xxxx.edu.cn/findAbnormalList?dateStr={}'.format(today)
print(" [+]目标url:"+detail_url)
sleep(1)
print(" [+]正在从url获取json数据")
sleep(1)
detail_page_text = se.get(url=detail_url, headers=headers).text
result = detail_page_text
print(" [+]已成功获取json数据,并保存至stu.json")
sleep(0.5)
#print(result)
with open('stu.json', 'w', encoding='utf-8')as fp:
fp.write(result)
读取stu.json并处理数据
首先单独执行这个函数,去查看获取的json数据格式。
其实我们关注的只有姓名和电话,因为我们最终要达到的效果是这样的。
针对这个去处理数据,并且转换成两个列表,list这个数据格式有个好处,就是他的元素是具有唯一性的,也就是说我省了数据去重的操作。
def list_format():
data = ''
with open('stu.json', 'rb') as f:
data = f.read()
stuInfo = json.loads(data)
a = stuInfo.items()
total = int(int(str(stuInfo["total"])) / 2)
print(' [+]共获取学生' + str(total) + '人')
sleep(0.8)
#初始化一个字典
stuNamePhone = {}
#这里就是处理json,提取姓名和电话的方法
for item in stuInfo['rows']:
name = item['xm']
# print(name)
phoneNum = item['sj']
stuNamePhone[name] = phoneNum
#print(stuNamePhone.items())
name_ = list(stuNamePhone.keys())
phone_ = list(stuNamePhone.values())
print(" [+]即将发送提醒短信")
sleep(2)
#为了方便,我直接在这里调用了发送短信的接口(send_sms()这个函数就是)
#至于那个_fake(),就是测试用的,只打印提示信息,不发送
for i in range(0, total):
#send_sms_fake(phone_[i], name_[i])
sleep(0.2)
send_sms(phone_[i],name_[i])
好的,结束了这部分代码,就完成调用了send_sms()函数,发信息成功了。
发短信函数
如上文,这里是用的腾讯云的现成的东西
# send_sms为调用腾讯云api,进行发送信息,使用前,请先去除apikey前的注释号
def send_sms(phoneNum, name):
try:
# 下面这一列注释符去掉才能正常使用,避免误操作导致大量发信
cred = credential.Credential("这里是", "api key的位置")
httpProfile = HttpProfile()
httpProfile.endpoint = "sms.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = sms_client.SmsClient(cred, "ap-beijing", clientProfile)
req = models.SendSmsRequest()
params = {
"PhoneNumberSet": [phoneNum],
"SmsSdkAppId": "",
"SignName": "这里是签名,直接复制你过审的签名过来",
"TemplateId": "1",
"TemplateParamSet": [name] # 这是变量位置,我的就是同学的名字
}
req.from_json_string(json.dumps(params))
resp = client.SendSms(req)
print(resp.to_json_string())
except TencentCloudSDKException as err:
print(err)
# send_sms_fake为打印姓名+电话,仅用于测试
def send_sms_fake(phoneNum, name):
print(" [+]"+phoneNum+" "+ name + " 模拟短信发送成功")
最后把所有的import统一出来
import datetime
import json
from time import sleep
import requests
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.sms.v20210111 import sms_client, models
sleep()函数没有实际性用途,是因为这个如果不加sleep,整个脚本可能一秒钟就结束了,我个人的观感不大好,就加了sleep美化一下
ok,看一下效果。
问了几个同学,确实收到了短信
测试成功,顺带被夸夸
开开心心,收工!