Python使用Request库实现PC端学小易(适用app版本1.0.6)

Python使用Request库实现PC端学小易app(适用app版本1.0.6)

  • 前言
  • 抓包
    • 登录操作抓包
    • 搜题操作抓包
    • 数据分析
      • 登录
      • 搜题
      • 重点
  • 代码实现
    • 导入库
    • tkinter实现简易图形界面部分
    • request库实现登录部分
    • 搜题部分
    • 整理输出至tkinter部分
    • 完整代码
  • 重点

前言

一直以来学小易只有安卓段与IOS端的app,在PC端没有开发软件,本文通过对安卓版的学小易app进行抓包获取数据,然后通过python以tkinter图形界面使用Request库模拟请求,进行模拟登录与查题操作。

抓包

登录操作抓包

在学小易登录界面抓包得到登录所需要的请求头以及获得的Response.text:
请求头
Response

搜题操作抓包

抓包

数据分析

登录

抓包数据显示,学小易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更新后请不要再使用本段代码。

热门文章

暂无图片
编程学习 ·

taro开发微信小程序 -- 下拉刷新和上拉加载

参考文档:https://taro-docs.jd.com/taro/docs/tutorial#%E9%A1%B9%E7%9B%AE%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84其实和微信小程序一样,只要设置页面配置信息并添加对应函数即可class Index extends Component {// 添加小程序页面配置信息config: {enablePullDownRefresh: …
暂无图片
编程学习 ·

LeetCode_Everyday:021 Merge Two Sorted Lists

LeetCode_Everyday:021 Merge Two Sorted Lists题目:示例:代码参考此外 LeetCode Everyday:坚持价值投资,做时间的朋友!!! 题目: 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例:示例 1:输入:1->2->4, 1-…
暂无图片
编程学习 ·

怎么才能最短时、高效、踏实的学习 Python?

作者:飞绝眷岭 链接:https://www.zhihu.com/question/28530832/answer/58656332 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。Dataquest 这个网站上提供了一系列和数据分析相关的python教程,从python基本语法到data analysis的基本函数…
暂无图片
编程学习 ·

unordered_map/unorderd_set使用与哈希介绍

在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 O(logN),即最差情况下 需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次 数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的…
暂无图片
编程学习 ·

综合练习

一、端午节的淘宝粽子交易 import pandas as pd import numpy as npdf1 = pd.read_csv(zongzi.csv) df1.head()标题价格付款人数店铺发货地址0五芳斋粽子礼盒 心悦+18只装咸鸭蛋组合端午节礼品团购嘉兴肉粽子1296人付款五芳斋官方旗舰店浙江 嘉兴1北京稻香村端午粽子手工豆沙粽…
暂无图片
编程学习 ·

阿里云香港云服务器ECS适合什么场景?

香港云服务器有什么好处?适合什么场景呢?许多对于不想备案的用户,那么香港节点最为合适不过了。因此,笔者整理阿里云香港服务器优惠购买流程以及列出香港服务器的好处!阿里云香港服务器:目前阿里云优惠活动中,限时云服务器爆款2折有推出香港节点突发性t5实例 固定配置1核…
暂无图片
编程学习 ·

设计模式-工厂模式

关注公众号 JavaStorm 获取更多精彩工厂模式定义 工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化…
暂无图片
编程学习 ·

Java工具类-使用RSA验签

1 私钥签名public static String signByKey(String content,String privateKey) {PKCS8EncodedKeySpec sp = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));KeyFactory keyFactory = KeyFactory.getInstance("RSA");PrivateKey key = keyF…
暂无图片
编程学习 ·

深度学习入门教程-1.1 神经网络是什么

到底什么是人工神经网络?前面提到,人工神经网络是从大脑的理解中汲取灵感而形成的。在我们的大脑中,有数十亿个神经元,它们连接成了一个神经网络。人工神经网络,结构也有些类似。许多个神经元(下图中的⚪)相连,构成了一个神经网络。人类大脑神经元细胞接收来自外部多个…
暂无图片
编程学习 ·

Linux7.6快速安装tigervnc1.8

前置配置:配置yum环境:确保Linux7.6安装盘存在于光驱中(或虚拟光驱)mkdir /mnt/linux mount /dev/cdrom /mnt/linux cd /etc/yum.repos.d mkdir bk mv *.repo bk/ echo "[EL]" >> /etc/yum.repos.d/yuminstall.repo echo "name =Linux 7.x DVD"…
暂无图片
编程学习 ·

openfeign 转发header 实现全链路灰度发布

openfeign 转发header 实现全链路灰度发布引言实现在服务中修改Predicate加一个Interceptor配置ribbon规则,使用我们自己的规则 引言网关层看这里 之前写了网关层实现灰度发布,但是这个只能被路由一次 像是这样: 客户端->网关->根据版本号路由到应用 之后应用再调用其…
暂无图片
编程学习 ·

SwiftUI 2.0 实现无限滚动的分页列表(高性能含源码)

本文价值与收获 看完本文后,您将能够作出下面的界面实战需求 我们平时构建的应用基本上都是列表类应用,例如待办事项列表、微博、微信朋友圈和视频列表等。这些列表都是可以无限滚动的,那这个功能该如何实现呢。本篇文章将告诉大家个非常简单高效的构建无限滚动List的方法,…
暂无图片
编程学习 ·

LeetCode 237. 删除链表中的节点

目录结构1.题目2.题解1.题目请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。现有一个链表 -- head = [4,5,1,9],它可以表示为:示例:输入: head = [4,5,1,9], node = 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那…
暂无图片
编程学习 ·

企业实战--kubernetes(九)---存储(Secret)

一、Secret简介 Secret对象类型用来保存敏感信息,例如密码、OAuth令牌和ssh key。 敏感信息放在Secret中比放在Pod的定义或者容器镜像中来说更加安全和灵活。 Pod可以用两种方式来使用Secret: 作为volume中的文件被挂载到pod中的一个或多个容器中。 当kubelet为pod拉取镜像时…
暂无图片
编程学习 ·

平面扫描(Plane-sweeping)介绍

平面扫描(Plane-sweeping)介绍: Reference:三维重建之平面扫描算法(Plane-sweeping) plane-sweeping算法在三维重建中非常重要,其特别适合并行计算,因此通过GPU加速后可以使复杂的稠密重建达到实时。大多实时三维重建的深度图生成部分采用plane-sweeping算法。而且plane…
暂无图片
编程学习 ·

tensorflow-serving布置facenet心得

这个的东西困扰我很久,终于弄成了。不知道我做的是不是太繁琐,如果有人做的更简单,希望指出,谢谢。docker中,做了两个容器,一个放的mtcnn,一个放的facent。他们并不是多模型布置的。mtcnn其中包括:pnet,rnet和onet,这三个是多模型布置。客户端通过调用mtcnn,得到返回…
暂无图片
编程学习 ·

【Pytorch】Mode存储

1. 保存加载模型权重pytorch保存数据的格式为.t7文件或者.pth文件,t7文件是沿用torch7中读取模型权重的方式。而pth文件是python中存储文件的常用格式。而在keras中则是使用.h5文件。# 保存模型示例代码 print(===> Saving models...) state = {state: model.state_dict(),…