构思

整体思路

通过API调用获取错题信息,将数据存入数据库(保证可长期查看),通过python+django将数据库数据在前端呈现。

实施思路

1、通过抓包获取到小程序相对应的错题集API信息(url、path、参数、请求方法),分析响应信息,分析获取API之间的关联关系,输出API文档。 2、通过数据关系,搭建数据库结构,创建数据库表。 3、设计方法类:   1)调用API,获取响应数据   2)将响应数据存入数据库   3)后端读取数据库数据,并返回前端界面。   4)前端设计错题以卡片的形式展示错题集。

实施过程

调用API获取响应数据

  抓包获取API信息

  使用charles进行抓包,因为小程序使用的是HTTPS请求,此处代理需要开启HTTPS代理请求,建议开启全局代理(即计算机代理),其实在开始charles时会默认开启。在此需要设置HTTPS的代理端口。     
  【问题】   1、抓包小程序需要清理小程序缓存,查询WMPFRuntime文件夹所在位置,删除该文件夹即可。   2、抓包小程序过程中,总提示小程序认证失败,再次登录却提示“设备参数缺失”,此时可以先退出charles,登陆后再打开charles,即可正常获取认证信息。   3、获取的API信息,其中的token,在python设置参数时需要进行url解码,再进行请求。

 python发送HTTPS请求

  

 1 def GetExercisebook():
 2     # SQL集
 3 
 4     pmpExerciseManualInsertSQL = "INSERT INTO `mysql_python`.`pmp_exercise_manual` (`id`, `history_id`, `subject`, `name`, `exam_type_id`) VALUES (%s, %s, %s, %s, %s);"
 5     pmpManualSubjectInsertSQL = "INSERT INTO `mysql_python`.`pmp_manual_subject` (`manual`, `subject`) VALUES (%s, %s);"
 6     pmpSubjectInsertSQL = "INSERT INTO `mysql_python`.`pmp_subject` (`subjectId`, `type_name`, `content`, `opt`, `correct_answer`, `analysis`, `label_text`) VALUES (%s, %s, %s, %s, %s, %s, %s);"
 7 
 8     # token 值(不同设备登陆后需要更新此部分)
 9     TOKEN_NEW = '********'
10 
11     # 获取问题集---请求参数params
12     VALUE = {"clienttype": "2",
13              "exam_type": "****",
14              "isBusy": "true",
15              "isOver": "false",
16              "loadingTips": "*******",
17              "login_status": "2",
18              "page": "0",
19              "pageSize": "100",
20              "login_status": "2",
21              "token": TOKEN_NEW
22              }
23     url = "https://*******/api/v2/****/****/history/exercise"
24     payload = ""
25     headers = {
26     }
27     print("*********************开始发送请求********************")
28     ResponseProblemBook = requests.request("GET", url, headers=headers, data=payload, params=VALUE)
29     print(ResponseProblemBook.request)
30     ProblemBook = ResponseProblemBook.json()
31     subjectDetailList = ProblemBook['data']
32     print("*********************获取响应成功********************")
33 
34     # 遍历获取习题信息
35     num = 0
36     pmpExerciseManualInsertVal = []
37     for num in range(len(subjectDetailList)):
38         pmpManualSubjectInsertVal = []
39         pmpSubjectInsertVal = []
40         id = subjectDetailList[num]['id']
41         history_id = subjectDetailList[num]['history_id']
42         name = subjectDetailList[num]['name']
43         exam_type_id = subjectDetailList[num]['exam_type_id']
44         subject = subjectDetailList[num]['subject']
45         bSubject = qieString(subject)
46         for subjectId in bSubject:
47             pmpManualSubjectInsertResult = (id, subjectId)
48             pmpManualSubjectInsertVal.append(pmpManualSubjectInsertResult)
49             url_2 = "https://*******/api/v2/****/****/" + subjectId + "/analysis"
50             VALUE_2 = {"id": subjectId,
51                        "exam_type": "****",
52                        "clienttype": "2",
53                        "login_status": "2",
54                        "token": TOKEN_NEW
55                        }
56             response_2 = requests.request("GET", url_2, headers=headers, data=payload, params=VALUE_2)
57             response_b = response_2.json()
58             subjectDetailList_b = response_b['data']
59             type_name_b = subjectDetailList_b['type_name']  # 题目类型
60             correct_answer_b = subjectDetailList_b['correct_answer']  # 答案
61             content_b = subjectDetailList_b['content']  # 问题
62             analysis_b = subjectDetailList_b['analysis']  # 分析
63             opt_b = subjectDetailList_b['opt']  # 选项
64             label_text_b = subjectDetailList_b['label_text']  # 分类标准
65             pmpSubjectInsertResult = (
66                 subjectId, type_name_b, content_b, opt_b, correct_answer_b, analysis_b, label_text_b)
67             pmpSubjectInsertVal.append(pmpSubjectInsertResult)
68         Save_Mysql(pmpManualSubjectInsertSQL, pmpManualSubjectInsertVal)
69         Save_Mysql(pmpSubjectInsertSQL, pmpSubjectInsertVal)
70         print('获取关联表成功:', pmpManualSubjectInsertVal)
71         pmpExerciseManualInsertResult = (id, history_id, subject, name, exam_type_id)
72         pmpExerciseManualInsertVal.append(pmpExerciseManualInsertResult)
73         # print('获取习题集成功:', name)
74         num = num + 1
75     Save_Mysql(pmpExerciseManualInsertSQL, pmpExerciseManualInsertVal)

 

将响应值存入数据库

 1 # 将题目存入数据库,无返回值
 2 def Save_Mysql(sql, val):
 3     HOST = 'localhost'
 4     PORT = 3306
 5     USER = 'root'
 6     PASSWORD = 'XF950701.'
 7 
 8     connection = pymysql.connect(host=HOST, port=PORT, user=USER, password=PASSWORD, db='mysql_python', charset='utf8')
 9     try:
10         cursor = connection.cursor()
11 
12         cursor.executemany(sql, val)
13         connection.commit()
14         print("插入成功数据:", cursor.rowcount)
15     except Exception:
16         print("插入失败")
17     finally:
18         cursor.close()  # 关闭游标连接
19         connection.close()
20 
21 
22 # 读取数据库数据输出表格
23 def qieString(subject):
24     aString = subject
25     bString = re.sub(u"\\[|\\]", '', aString)
26     cString = bString.split(',')
27     return cString

 

获取数据库数据

 1 def GetPnP(sql):
 2     HOST = 'localhost'
 3     PORT = 3306
 4     USER = 'root'
 5     PASSWORD = 'XF950701.'
 6 
 7     conn = pymysql.connect(host=HOST, port=PORT, user=USER, password=PASSWORD,
 8                            db='mysql_python',
 9                            charset='utf8')
10     # 生成sql语句 利用传入的BankConsentId进行条件查询
11     authorize_sql = sql
12     cursor = conn.cursor()
13     try:
14         cursor.execute(authorize_sql)
15         desc = cursor.description  # 获取字段的描述
16         data = [dict(zip([col[0] for col in desc], row)) for row in
17                 cursor.fetchall()]
18     except:
19         conn.rollback()
20     cursor.close()
21     conn.close()
22     return data

 

将数据格式化后返回前端页面

 1 def GetExercisebook(request):
 2     p1 = "<p>|</p>"
 3     from Myapp.Dao.HttpsRequest.getpnp import GetPnP
 4 
 5     authorize_sql = "SELECT Id,subjectId,type_name,content,correct_answer,analysis FROM pmp_subject"
 6     data = GetPnP(authorize_sql)
 7     context = {"code": 200, "data": data}
 8     JsonDupsContext = json.dumps(context)
 9     opt_demo = re.sub(p1, '', JsonDupsContext)
10     JsonLoadsContext = json.loads(opt_demo)
11     return render(request, 'Exercisebook.html', JsonLoadsContext)
12 
13 
14 def GetOpt(request):
15     p1 = "<p>|</p>"
16     from Myapp.Dao.HttpsRequest.getpnp import GetPnP
17     subjectId = request.GET['subjectId']
18     authorize_sql = "SELECT opt FROM pmp_subject where subjectId = "+subjectId
19     opt =GetPnP(authorize_sql)
20     opt_demo = re.sub(p1, "", opt[0]['opt'])
21     context = json.loads(opt_demo)
22     return JsonResponse(context, safe=False)

 

前端呈现

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5 
 6 
 7     <title>Exercisebook</title>
 8     <meta charset="UTF-8">
 9         <title>卡片效果</title>
10         <style type="text/css">
11             .cardBox {
12                 width: 200px;
13                 box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
14                 text-align: center;
15                 float: left;
16                 margin-right: 10px;
17                 padding: 5px;
18                 padding-top: 15px;
19             }
20 
21             .headerBox {
22                 color: #fff;
23                 padding: 10px;
24                 font-size: 15px;
25                 height: 60px;
26             }
27 
28             .bodyBox {
29                 padding: 10px;
30             }
31 
32             .bodyBox p {
33                 margin-left: 5px;
34             }
35         </style>
36     <script>
37         function GetOpt(data) {
38             var subjectId = data;
39             $.get('/GetOpt/',{
40                 'subjectId': subjectId
41             },function (ret){
42                 var a = "";
43                 opt = ret;
44                 console.log(opt)
45                 for(var i = ret.length -1; i >=0 ; i--) {
46                     a = ret[i]['label']+":"+ret[i]['content']+'<br>'+a;
47                 document.getElementById(subjectId).innerHTML=a;
48             }
49             }
50             )
51         }
52     </script>
53     </head>
54 </head>
55 <body>
56 <div style="text-align: center;width: 90%">
57     <div hidden='1000'>
58         <h1 style="text-align: center">错题集 </h1>
59     </div>
60 
61     <div style="text-align: center;width: 100%">
62         <div style="text-align: center">
63             {% for List in data %}
64             <div class="cardBox" style="text-align:center;width: 22%;height: 600px">
65 
66             <!--卡片头部标签-->
67                 <div class="headerBox" style="background-color: #3c5bc2;height: 40px">
68                     <p>
69                         <a title="查看详情" style="cursor: pointer; color:white">第{{ List.Id }}题({{ List.type_name }})</a>
70                     </p>
71                 </div>
72                 <!--题目-->
73                 <div class="bodyBox">
74                     <p style="text-align: left">{{ List.content }}</p>
75                 </div>
76                 <!--选项-->
77                 <div class="bodyBox">
78                     <script>
79                     window.onload=GetOpt({{ List.subjectId }});
80                     </script>
81                     <p id="{{ List.subjectId }}" style="text-align: left">
82                     </p>
83                 </div>
84                 <!--答案 分析-->
85                 <div class="bodyBox">
86                     <p style="text-align: left">{{ List.correct_answer }}</p>
87                     <br>
88                     <p style="text-align: left">{{ List.analysis }}</p>
89                 </div>
90             </div>
91             {% endfor %}
92         </div>
93     </div>
94 </div>
95 
96 </body>
97 </html>