Python使用Request库实现PC端学小易app(适用app版本1.0.6)
- 前言
- 抓包
- 登录操作抓包
- 搜题操作抓包
- 数据分析
- 登录
- 搜题
- 重点
- 代码实现
- 导入库
- tkinter实现简易图形界面部分
- request库实现登录部分
- 搜题部分
- 整理输出至tkinter部分
- 完整代码
- 重点
前言
一直以来学小易只有安卓段与IOS端的app,在PC端没有开发软件,本文通过对安卓版的学小易app进行抓包获取数据,然后通过python以tkinter图形界面使用Request库模拟请求,进行模拟登录与查题操作。
抓包
登录操作抓包
在学小易登录界面抓包得到登录所需要的请求头以及获得的Response.text:
搜题操作抓包
数据分析
登录
抓包数据显示,学小易app的登录操作即向https://app.51xuexiaoyi.com/api/v1/login发送username和password两个参数,服务器收到后Response用户的唯一的api_token。
搜题
搜题操作是向https://app.51xuexiaoyi.com/api/v1/searchQuestion发送keyword,服务器收到后Response题目与答案
重点
登录时学小易app将账号的识别码(唯一的api_token)发回设备的同时,还获取了设备的识别码,即手机的deviceToken。在使用下面代码前,需要自己使用HTTPCanary对自己手机上的学小易app进行抓包,找到自己手机的deviceToken并填入下面代码中的device字符串里。这样学小易才会认为你的电脑和手机是同一个设备在进行搜索。在代码中,将device参数留空同样可以使用代码成功搜题,但是有较大可能会让学小易检测到并对账号进行一天冻结。
代码实现
导入库
import tkinter
import requests
import json
from js2py import eval_js
import re
tkinter实现简易图形界面部分
class win1():
def __init__(self):
def login_t():
usernm = usernm1.get()
passwd = passwd1.get()
win.destroy()
Mooc.login(usernm, passwd)
win = tkinter.Tk()
win.title("学小易搜题PC版 By Samuel")
win.resizable(0, 0)
usernm1 = tkinter.Entry(win,width=30)
usernm1.grid(column=1,row=0)
passwd1 = tkinter.Entry(win,width = 30)
passwd1.grid(column=1,row=1)
tag1 = tkinter.Label(win,text="账号")
tag1.grid(row=0,column=0)
tag2 = tkinter.Label(win,text="密码")
tag2.grid(row=1,column=0)
btn = tkinter.Button(win,text="登录",command=login_t)
btn.grid(row=0,column=2,rowspan=2)
win.mainloop()
class w2():
def __init__(self):
global txt
def check():
text = test2.get()
Mooc.check_test(text)
win2 = tkinter.Tk()
win2.title("学小易搜题PC版 By Samuel")
win2.resizable(0, 0)
test2 = tkinter.Entry(win2, width=30)
test2.grid(column=1, row=0)
tag1 = tkinter.Label(win2, text="输入题目")
tag1.grid( column=0,row=0)
btn = tkinter.Button(win2, text="查询", command=check,width=20)
btn.grid(row=0, column=2, rowspan=2)
txt = tkinter.Text(win2,)
txt.grid(row=2,column=0,rowspan=10,columnspan=3)
win2.mainloop()
request库实现登录部分
def login(self,usernm,passwd):
headers = {
'platform': 'android',
'app-version': '1.0.6',
'content-type': "application/json; charset=utf-8",
'accept-encoding': 'gzip',
'user-agent': 'okhttp/3.11.0'
}
data = {
"username": usernm,
"password": passwd
}
url = 'https://app.51xuexiaoyi.com/api/v1/login'
resq = requests.post(url,headers=headers,json=data)
text = resq.text
text_list=text.split(",")
if "200" in text_list[0]:
api_token = re.findall('api_token":"(.*?)"', text_list[2])[0]
userid = re.findall('userid":"(.*?)"', text_list[3])[0]
f = open('token.txt','w')
f.write("api_token:"+api_token+"\n"+"userid:"+userid)
f.close()
wi2 = w2()
else:
print("登录失败,请检查您的账号密码是否正确")
w1=win1()
搜题部分
def check_test(self,keyword):
f = open('token.txt','r')
content = f.read()
tokens = re.findall("api_token:(.*?)\n",content)[0]
url = "https://app.51xuexiaoyi.com/api/v1/searchQuestion"
t = eval_js("new Date().getTime()")
t = str(t)
data = {
"keyword":keyword
}
headers ={
'token': tokens,
'device': '',#######!!!填入自己设备的deviceToken!!!###########
'platform': 'android',
'User-Agent': 'okhttp/3.11.0',
'app-version': '1.0.6',
't': t,
'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",
'Accept-Encoding': "gzip, deflate, br"
}
resq = requests.post(url, headers=headers, data=data)
result = resq.json()
code = re.findall("'code': (.*?)",result)
if code == "500":
result="账号异常,请联系管理员或代码作者更新"
txt.delete("0.0","end")
txt.insert("insert",result)
elif code == "200":
self.analysis(result)
else:
print("网络连接异常,请检查网络连接")
整理输出至tkinter部分
def analysis(self,content):
content = str(content)
results = re.findall("{'q': '(.*?)', 'a': '(.*?)'}", content)
for item in results:
txt.insert('insert', item[0] + "\n" + "答案:" + item[1] + "\n")
完整代码
import tkinter
import requests
import json
from js2py import eval_js
import re
class xuexiaoyi():
def analysis(self, content):
content = str(content)
results = re.findall("{'q': '(.*?)', 'a': '(.*?)'}", content)
txt.delete('0.0', 'end')
if len(results) != 0:
for item in results:
txt.insert('insert', item[0] + "\n" + "答案:" + item[1] + "\n")
else:
txt.insert('insert', "未找到答案,请重新修改关键词")
def login(self,usernm,passwd):
headers = {
'platform': 'android',
'app-version': '1.0.6',
'content-type': "application/json; charset=utf-8",
'accept-encoding': 'gzip',
'user-agent': 'okhttp/3.11.0'
}
data = {
"username": usernm,
"password": passwd
}
url = 'https://app.51xuexiaoyi.com/api/v1/login'
resq = requests.post(url,headers=headers,json=data)
text = resq.text
text_list=text.split(",")
if "200" in text_list[0]:
api_token = re.findall('api_token":"(.*?)"', text_list[2])[0]
userid = re.findall('userid":"(.*?)"', text_list[3])[0]
f = open('token.txt','w')
f.write("api_token:"+api_token+"\n"+"userid:"+userid)
f.close()
wi2 = w2()
else:
print("登录失败,请检查您的账号密码是否正确")
w1=win1()
def check_test(self,keyword):
f = open('token.txt','r')
content = f.read()
tokens = re.findall("api_token:(.*?)\n",content)[0]
url = "https://app.51xuexiaoyi.com/api/v1/searchQuestion"
t = eval_js("new Date().getTime()")
t = str(t)
data = {
"keyword":keyword
}
headers ={
'token': tokens,
'device': '', #######!!!填入自己设备的deviceToken!!!#######
'platform': 'android',
'User-Agent': 'okhttp/3.11.0',
'app-version': '1.0.6',
't': t,
'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",
'Accept-Encoding': "gzip, deflate, br"
}
resq = requests.post(url, headers=headers, data=data)
result = resq.json()
code = result['code']
if code == 500:
result="账号异常,请联系管理员或代码作者更新"
txt.delete("0.0","end")
txt.insert("insert",result)
elif code == 200:
self.analysis(result)
else:
result="网络连接异常,请检查网络连接"
txt.delete("0.0","end")
txt.insert("insert",result)
class win1():
def __init__(self):
def login_t():
usernm = usernm1.get()
passwd = passwd1.get()
win.destroy()
Mooc.login(usernm, passwd)
win = tkinter.Tk()
win.title("学小易搜题PC版By_CSDN_Samuel三木耳")
win.resizable(0, 0)
usernm1 = tkinter.Entry(win,width=30)
usernm1.grid(column=1,row=0)
passwd1 = tkinter.Entry(win,width = 30)
passwd1.grid(column=1,row=1)
tag1 = tkinter.Label(win,text="账号")
tag1.grid(row=0,column=0)
tag2 = tkinter.Label(win,text="密码")
tag2.grid(row=1,column=0)
btn = tkinter.Button(win,text="登录",command=login_t)
btn.grid(row=0,column=2,rowspan=2)
win.mainloop()
class w2():
def __init__(self):
global txt
def check():
text = test2.get()
Mooc.check_test(text)
win2 = tkinter.Tk()
win2.title("学小易搜题PC版By_CSDN_Samuel三木耳")
win2.resizable(0, 0)
test2 = tkinter.Entry(win2, width=30)
test2.grid(column=1, row=0)
tag1 = tkinter.Label(win2, text="输入题目")
tag1.grid( column=0,row=0)
btn = tkinter.Button(win2, text="查询", command=check,width=20)
btn.grid(row=0, column=2, rowspan=2)
txt = tkinter.Text(win2,)
txt.grid(row=2,column=0,rowspan=10,columnspan=3)
win2.mainloop()
if __name__ == '__main__':
try:
Mooc = xuexiaoyi()
f = open('token.txt', 'r')
content = f.read()
f.close()
t = re.findall("api_token:(.*?)\n", content)
if len(t) != 0:
wi2 = w2()
except:
w1= win1()
重点
- headers中的device参数最好是自己抓包得到的自己手机的deviceToken,否则如果手机电脑两个设备不断的切换着使用,学小易会检测到。device参数留空同样能用,但基本上查了一两次账号就会被冻结,得不偿失。
- 代码运行一次执行登录操作之后,会在代码源目录生成一个token.txt文件,里面记录了使用者账号的userid和api_token,下次运行代码时便会跳过登录操作,直接使用api_token开始搜题
- 不要频繁的进行登录操作,包括但不限于(在两个设备间不断的进行切换操作,在一个设备不断的进行登录request)
- 该代码现在仅局限于1.0.6版本使用 ,app更新后请不要再使用本段代码。