0%

再见了,青马易战

v-1.0 少年, 快送自己一个青马易战自动答题机吧!

免责声明: 本文仅为个人学习交流日志, 禁止复制/下载等内容迁移行为!

起因

又是网课摸鱼的一天, 我在QQ空间上冲浪, 突然刷到同学转发的”青马易战代刷”. 只要支付7R~12R, 对方就会帮你刷完一科500题的青马易战.

哦, 我才突然想起来还有这么一个作业要做, “其实青马的题挺好做的, 500题也就是一天半天的样子.”—来自其他同学. 那其实自己做也没什么大不了的嘛, 何必请人去刷呢.

当我亲自做过大半百题后终于醒悟, 自己还是年轻了. 青马易战的题目并不好做, 需要不浅的知识储备. 少年, 快送自己一个青马易战自动答题机吧! 果然比起交钱给别人, 还是用自己双手解决作业更加殷实.

简单搜索之后, 喜得前辈真传.

青马易战解决方案-QingmaKiller - Tawn's Blog

python实现青马易站自动刷题_跳墙网

理论存在, 实践开始! 虽然前辈说的东西完全没有接触, 但是看起来不难, 似乎是爬虫技术? 那就开学吧!

2021年最新Python爬虫教程+实战项目案例(最新录制)_哔哩哔哩_bilibili

前置

HTTP

当你与网页交互时, 浏览器会通过http协议与服务器沟通, 你说你想要的, 然后服务器发给你想要的.

你发出去的叫请求报文, 请求报文由三个部分构成:

请求行 -> 请求方式 请求url地址 协议

首部行 -> 附加信息

请求体 -> 请求参数

你接受到的叫响应报文, 响应报文由三个部分构成:

状态行 -> 协议 状态码

首部头 -> 附加信息

响应体 -> 响应数据

其中比较重要的是首部行中的:

  1. User-Agent: 你的访问设备, 记得伪装成手机哦
  2. cookie: 用户的登入信息, 就是标识你是你啦

想要监控访问过程的报文可以直接浏览器’右键→检查→网络’查看

Untitled
Untitled

Python

不会Python怎么办? 菜鸟速通看一看! Python支持中文变量真的很棒.

只要了解一下基础的流程控制就好了. 然后了解一下字典/JSON/文件操作, 用来存储答案.

Python3 教程 | 菜鸟教程

Python Requests

选用Python的原因就在于此了, Requests可以非常方便的用来发送THHP请求

1
import requests

记得伪装吗, 先定义一下首部行

1
2
3
4
header = {
"User-Agent": "XXX",
'Cookie': "XXX"
}

然后就可以访问网站啦

1
2
res = requests.get(url = url, headers = header, params=param)
res = requests.post(url = url, headers = header, data = data)

用JSON提取出响应报文中的数据

1
2
3
import json

数据 = json.loads(res.text)

正则表达式

拿到网页之后会需要从中找到的内容

1
import re

开发

获取cookie

参考了前辈的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 获取cookie
def updateCookie(self):
headers = {
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Host': 'qm.linyisong.top',
'Proxy-Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'User-Agent': 省略
}
res = requests.get(self.url, headers=headers)
headers['Cookie']=re.match(r'JSESSIONID=\w*',res.headers['set-cookie']).group()
res = requests.get(self.url, headers=headers)
self.cookie=headers['Cookie']
print('成功取得cookie')
print(self.cookie)
res.close()

捕获通信

先随便开一门做两题, 监控一下网络通信

发现主要有两个通信行为, 一个nextSubject, 一个changeSituation, 看一下.

在nextSubject里

请求头

1
2
3
4
5
6
7
8
9
10
11
12
POST /yiban-web/stu/nextSubject.jhtml?_=省略 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Content-Length: 10
Content-Type: application/x-www-form-urlencoded
Cookie: 省略
Host: qm.linyisong.top
Origin: http://qm.linyisong.top
Proxy-Connection: keep-alive
Referer: http://qm.linyisong.top/yiban-web/stu/toSubject.jhtml?courseId=7
User-Agent: 省略

请求参数

1
courseId=7

响应数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"data": {
"nextSubject": {
"courseId": 7,
"option0": "主张统一",
"option1": "主张独立",
"option2": "不统不独",
"option3": "一国两府",
"optionCount": 4,
"score": 1,
"subDescript": "当前国民党对两岸关系是",
"subType": "单选题",
"timeLimit": 60
},
"uuid": 省略
},
"isSuccess": true
}

在changeSituation里

请求头

1
2
3
4
5
6
7
8
9
10
11
12
POST /yiban-web/stu/changeSituation.jhtml?_=省略 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Content-Length: 69
Content-Type: application/x-www-form-urlencoded
Cookie: 省略
Host: qm.linyisong.top
Origin: http://qm.linyisong.top
Proxy-Connection: keep-alive
Referer: http://qm.linyisong.top/yiban-web/stu/toSubject.jhtml?courseId=7
User-Agent: 省略

请求参数

1
2
3
answer: A
courseId: 7
uuid: 省略

响应数据

1
2
3
4
5
6
7
8
{
"data": {
"rightAnswer": false,
"rightOption": "C.不统不独"
},
"isSuccess": true,
"message": "回答错误!"
}

分析通信

多做几题对比之后, 可以发现:

请求行中的数字是时间戳

同样参考前辈的函数获取时间戳

1
2
3
4
5
6
7
# 获取时间戳
def getTime():
t = str(time.time())
a = str(t[0:10])
b = str(t[11:14])
res = a + b
return res

使用courseId请求题目

在请求题目时, 只要在参数附带courseId就可以得到完整的题目报文, 包括题面选项和类型

1
2
3
4
5
6
7
8
看题链接 = 'http://qm.linyisong.top/yiban-web/stu/nextSubject.jhtml?_={}'.format(getTime())
headers = {
'Cookie': cookie,
}
让我看看 = {
'courseId': courseId
}
res = requests.post(看题链接, headers = headers, data = 让我看看)

在响应报文里我们可以得到我们想要的题目和下面要用到的UUID

1
2
uuid = json.loads(res.text)['data']['uuid']
题目 = json.loads(res.text)['data']['nextSubject']

使用UUID回答题目

在回答题目时, 需要在参数中附带答案, courseId和UUID

答案只是ABCD, 即使是判断题, 也是用AB表示的

courseId直接同科目即可

UUID是题目报文中的一部分, 从中获取

1
2
3
4
5
6
7
8
9
10
答题链接 = 'http://qm.linyisong.top/yiban-web/stu/changeSituation.jhtml?_={}'.format(getTime())
headers = {
'Cookie': cookie,
}
让我猜猜 = {
'answer': 答案,
'courseId': courseId,
'uuid': uuid
}
res_ = requests.post(答题链接, headers = headers, data = 让我猜猜)

在响应报文里我们可以得到正确答案和作答情况

1
2
正确答案 = json.loads(res_.text)['data']
作答情况 = json.loads(res_.text)['message']

正确做题

既然我们可以得到完整题目和正确答案, 又能提交自己想要的答案, 那么就可以收集题库了, 然后在做题时到题库中检索, 如果有答案就回答正确答案, 没有答案就随便回答一个吧

查询答案

用题目和答案建立一个字典, 然后在字典里查

这部分也不知道在使用Python时怎样更高效, 有大佬可以教教QWQ

1
2
3
4
5
6
7
8
if 题目 in 题库:
print('匹配答案!')
答案 = 题库[题目]
匹配到答案 = 1
else:
print('没有答案!')
答案 = 'A'
匹配到答案 = 0

录入题库

把获取到的题目写入文件本地保存

1
2
3
4
5
6
7
if 匹配到答案 == 0:
新题目 = {题目,正确答案}
with open('database_{}.json'.format(courseId), 'a+', encoding="utf-8") as f:
json.dump(新题目, f, ensure_ascii=False)
f.write('\n')
f.close()
print('题目入库!')

加载题库

把本地文件加载成题库

1
2
3
4
5
6
题库 = {}
with open('database_{}.json'.format(courseId), 'r', encoding="utf-8") as f:
for line in f.readlines():
题目 = json.loads(line)
f.close()
print('-->题库预载成功,有题目:'+str(len(题库))+'道<--')

自动化

最后, 把上述零件进行一个拼接就可以运行啦!

收集足够多的题目即可保证正确率.

后记

这是一次成果导向的学习, 不得不说在一个目标的指引下, 人会非常有动力. 不过迷茫也是真的迷茫, 全程百度和问人: “啊你会Python吗?”. 哈哈哈.

从上午十点钟有想法开始, 一直做到了凌晨三点, 晚饭甚至也不记得吃了奶茶没忘记喝, 虽然效率很低, 但是成就感很足.

在最后的凌晨几个小时里, 就是不断地调试一些BUG和优化打包了, 包括网络问题和文件读写, 就留给读者自己尝试吧.

虽然我是从不想给别人交钱开始, 但是在成品完成之后, 也有了商业化的想法, 但是果然还是太麻烦了吧, 我不想和陌生人讲话.

最后感谢前辈的文章, 首先可行先给我信心, 然后内容也给我帮助, 大佬太多啦, 我还是要努力才行.