Python使用Request库实现PC端学小易(适用app版本1.0.6)(经反映已不适用!!!)

    技术2022-07-10  289

    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__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() 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更新后请不要再使用本段代码。
    Processed: 0.014, SQL: 9