浦发信用卡自动签到 - 简单爬虫

顺利阅读本文需要一点 Web 知识、Chrome 使用、Python 语法。

浦发有个有趣的签到,需要回答一些问题,帮助银行更好的改善自己,每回答完一次都会有一次抽奖机会(一般是 88 积分),想抽奖但我又如此的懒,只好把这个做成自动化了。

链接如下(请自行复制): https://ecentre.spdbccc.com.cn/entry-voice-union/indexNewVoc.htm?channel=007&flag=true

开始正文前,先来了解一下整个的签到流程:

开始答题 -> 回答第一个题目 -> 回答下一个题目 -> 抽奖 -> 填写领取人信息。

后端接口的非常健壮,不用考虑直接提交最后一步的请求了。另外页面是服务端渲染,直接看 JSON 接口也不行。

做成完全自动的,那就照着上面的流程跟着把一个一个请求发出去就 OK 了。

开始答题

利用 Chrome 提供的调试工具,我们可以直接找到点击立即答题对应的相应事件:

对应的方法大致为:

location.href="toNewQuestion.htm?rid=xxx";

直接一个链接跳转,带了个 rid 参数,可能是本轮答题对应的一个 Session ID,也可能是用户打点跟踪 Track ID,没关系,我们发请求带上即可,我们的目的是完成一整套表单提交,不是搞清楚 rid 是什么。

涉及到的 Python 代码均有所删减,但不影响阅读。

startEntryUrl = f'{self.webHost}/entry-voice-union/indexNewVoc.htm?channel={self.channel}&flag=true'

self.defaultHeaders = {
  'Host': 'ecentre.spdbccc.com.cn',
  'User-Agent': 'xxx', # 记得带上 User-Agent,否则会触发反爬虫检测
  'Accept': 'text/html',
  'Accept-Language': 'zh-cn'
}

startEntryReq = requests.get(url=startEntryUrl, headers=self.defaultHeaders)
startEntryHtml = startEntryReq.text
self.recordId = re.search('rid=(.{36})', startEntryHtml, re.M | re.I).group().split('=')[1] # 提取 rid
self.cookies = startEntryReq.cookies
self.jsessionid = self.cookies['JSESSIONID'] # 记录 JSESSIONID
self.defaultHeaders['Cookie'] = f'JSESSIONID={self.jsessionid}'

回答问题

初看可能稍微麻烦一些,那就先来回答一次问题,看看发了啥请求,从这里入手看看:

默认设置下,页面重定向,Chrome 会清除上一页的请求记录,勾选 Preserve Log 即可保留全部请求记录。

Form Data 中的字段简单明了,这也对应着 Html 中 from 标签下的表单字段。

一个基本的选择语法即可拿到我们需要的字段的值:

questionId = toNewQuestionBf.select_one('input[name^="questionId"]').attrs['value']

如果你经常答题,会注意到题目有单选、多选、简答、打分,似乎还有图片上传,这我还没遇到过。

上图结合请求接口可以看到 answer 是回答的问题。

这样就好办了!

找一下 questionType 相关的内容可以知道: L 是打分,M 是多选,T 是简答,S 是单选。

多选稍微麻烦一些,需要再做个多选选项提取:

if questionType == 'M':
  values = [inputDom.attrs['value'] for inputDom in toNewQuestionBf.select('input[name^="answer"]')]

好了,对应的题目走心~~(随机)~~回答一下即可。 需要额外注意的是,提交表单格式为 multipart/form-data

对应的提交方法在这里:

0000 表示下一题,0001 表示问题回答完毕,准备提交用户信息。

这一步也可以通过分析完整的请求得出结论,什么方法都可以,解决问题即可。 补一个循环,直到返回 0001 就可以进入下一步了。

提交用户信息

抓一次请求的话,可以发现请求的数据是加密的,主要目的猜测是保护用户信息安全。

解决方案:

其他常见的加密问题,均可采取上述操作。

另外如果一定需要自己加密一下,Python 的处理:

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5

JavaScript 参见:https://github.com/travist/jsencrypt

后面的问题都是小事了。比如领红包和填写持卡信息。

最后还可以配在 CircleCI 上:

Next

上述例子是纯前端的操作,其他一些问题可能更为复杂,比如 Web 页面视频下载,这种情况产品会提供 App 下载的功能,直接从客户端下手即可,这也是有实际可以操作例子的。
总之动用所有可以用到的技术,总能搞定的。

上次更新: 1 个月前