new file: __pycache__/get_info.cpython-310.pyc

new file:   __pycache__/kill_course.cpython-310.pyc
	new file:   __pycache__/single_course.cpython-310.pyc
	modified:   course_info.json
	modified:   demo.py
	new file:   file.log
	modified:   get_info.py
	modified:   kill_course.py
	new file:   kill_quiz.py
	new file:   main.py
	modified:   single_course.py
main
JesterHey 11 months ago
parent 145bd5b71a
commit fffbd89ffd

@ -1 +1 @@
{"7366": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7033": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7517": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7032": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7516": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7031": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7515": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "6349": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7436": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7367": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "6937": {"rate": 0, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6938": {"rate": 0, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6991": {"rate": 0, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6992": {"rate": 68, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6993": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u672a\u8bc4\u5206"}, "6995": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7006": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7007": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7010": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7011": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7554": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7553": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7552": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7551": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7550": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7549": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7548": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7547": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7546": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7545": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7564": {"rate": 0, "type": "\u9009\u4fee", "status": "\u672a\u5b66\u5b8c"}}
{"6349": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7436": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7367": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7366": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7033": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7517": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7032": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7516": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7031": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7515": {"rate": 100, "type": "\u5fc5\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "6937": {"rate": 0, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6938": {"rate": 0, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6991": {"rate": 9, "type": "\u57f9\u8bad", "status": "\u672a\u5b66\u5b8c"}, "6992": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "6993": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "6995": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7006": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7007": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7010": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7011": {"rate": 100, "type": "\u57f9\u8bad", "status": "\u5df2\u5b66\u5b8c"}, "7554": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7553": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7552": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7551": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7550": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7549": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7548": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7547": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7546": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7545": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}, "7564": {"rate": 100, "type": "\u9009\u4fee", "status": "\u5df2\u5b66\u5b8c"}}

@ -1,4 +1,6 @@
import json
from loguru import logger
info = json.load(open('course_info.json', 'r'))
print(info)
# 自定义日志格式
logger.add("file.log", format="{time} {level} {message}", level="DEBUG")
logger.debug("这是一条具有自定义格式的日志信息")

@ -0,0 +1 @@
2024-03-10T12:13:35.955107+0800 DEBUG 这是一条具有自定义格式的日志信息

@ -3,57 +3,62 @@ from DrissionPage.common import *
from collections import *
import time
import json
from loguru import logger
# http://hnqmgc.17el.cn/grzx/
def decode(s):
return json.loads('"%s"' %s) # 由于页面源码中的json字符串是单引号包裹的所以需要转换成双引号包裹的json字符串
# 创建页面对象,并启动或接管浏览器
page = WebPage()
# 跳转到登录页面
page.get('http://hnqmgc.17el.cn/grzx/',retry=5,timeout=5,interval=1)
def get_info():
# 创建页面对象,并启动或接管浏览器
page = WebPage()
# 跳转到登录页面
try:
page.get('http://hnqmgc.17el.cn/grzx/',retry=5,timeout=5,interval=1)
except BaseException:
logger.error('网络连接失败')
# 定位到账号文本框,获取文本框元素
ele = page.ele('#userName') # #的意思是通过id定位元素
# 输入对文本框输入账号
ele.input('51140220050507901X')
# 定位到密码文本框并输入密码
page.ele('#password').input('hnqm123456')
# 定位到验证码文本框并输入验证码
inpcode = page.ele('#yzcode').text # 湖南青马太可爱了吧,验证码居然直接放在页面源码里:)
page.ele('#inpcode').input(inpcode)
# 点击登录按钮
page.ele('#btnLogin').click()
# 进入课程页面
page.ele('.wxtsBox-button').click() # 关闭提示页面
page.ele('#login_btn').click()
page.ele('@value=进入个人中心').click()
# 提取课程信息
time.sleep(2)
page.ele('@value=0').click()
# 获取总页数
course_info = {}
# <div class="paginationjs-nav J-paginationjs-nav">1 / 4</div>
# total_page = int(page.ele('.paginationjs-nav J-paginationjs-nav').text)
total_page = 4
for i in range(total_page):
# 逐页读取课程信息和完成状态并存放到字典中
tbodys = page.ele('#tbody')
# 获取tbody下的所有tr
trs = tbodys.eles('tag:tr')
for tr in trs:
cur_info = tr.text.split('\t')
course_id = cur_info[0] # 课程id
course_type = cur_info[2] # 课程类型
course_rate = int(cur_info[3][:-1]) # 课程完成百分比
course_status = cur_info[4] # 课程完成状态(是否完成习题/评价)
print(course_id, course_rate, course_status)
# 存放到字典中
course_info[course_id] = {'rate': course_rate,'type': course_type,'status': course_status}
if i != total_page - 1:
page.ele('@title=Next page').click()
time.sleep(1)
print(len(course_info))
# 写入json文件中
with open('course_info.json', 'w') as f:
json.dump(course_info, f)
# 定位到账号文本框,获取文本框元素
ele = page.ele('#userName') # #的意思是通过id定位元素
# 输入对文本框输入账号
ele.input('51140220050507901X')
# 定位到密码文本框并输入密码
page.ele('#password').input('hnqm123456')
# 定位到验证码文本框并输入验证码
inpcode = page.ele('#yzcode').text # 湖南青马太可爱了吧,验证码居然直接放在页面源码里:)
page.ele('#inpcode').input(inpcode)
# 点击登录按钮
page.ele('#btnLogin').click()
# 进入课程页面
page.ele('.wxtsBox-button').click() # 关闭提示页面
page.ele('#login_btn').click()
page.ele('@value=进入个人中心').click()
# 提取课程信息
time.sleep(2)
page.ele('@value=0').click()
# 获取总页数
course_info = {}
# <div class="paginationjs-nav J-paginationjs-nav">1 / 4</div>
# total_page = int(page.ele('.paginationjs-nav J-paginationjs-nav').text)
total_page = 4
for i in range(total_page):
# 逐页读取课程信息和完成状态并存放到字典中
tbodys = page.ele('#tbody')
# 获取tbody下的所有tr
trs = tbodys.eles('tag:tr')
for tr in trs:
cur_info = tr.text.split('\t')
course_id = cur_info[0] # 课程id
course_type = cur_info[2] # 课程类型
course_rate = int(cur_info[3][:-1]) # 课程完成百分比
course_status = cur_info[4] # 课程完成状态(是否完成习题/评价)
logger.info('课程id:{}|课程类型:{}|课程完成率:{}|课程完成状态:{}'.format(course_id,course_type,course_rate,course_status))
# 存放到字典中
course_info[course_id] = {'rate': course_rate,'type': course_type,'status': course_status}
if i != total_page - 1:
page.ele('@title=Next page').click()
time.sleep(1)
print(len(course_info))
# 写入json文件中
with open('course_info.json', 'w') as f:
json.dump(course_info, f)
if __name__ == '__main__':
get_info()

@ -1,39 +1,40 @@
from DrissionPage import ChromiumPage
from DrissionPage.common import *
from collections import *
import time
import json
# 统计课程完成情况
cnt = defaultdict(list)
# 读取课程信息
info = json.load(open('course_info.json', 'r'))
for k, v in info.items():
if v['rate'] < 100:
cnt[v['type']].append(k)
print(cnt)
# 按必修-选秀-专题-培训执行刷课
must = cnt['必修']
elective = cnt['选修']
special = cnt['专题']
train = cnt['培训']
while must or elective or special or train:
if must:
course_id = must.pop()
print('Kill course:', course_id)
# 执行刷课
# ...
if elective:
course_id = elective.pop()
print('Kill course:', course_id)
# 执行刷课
# ...
if special:
course_id = special.pop()
print('Kill course:', course_id)
# 执行刷课
# ...
if train:
course_id = train.pop()
print('Kill course:', course_id)
# 执行刷课
# ...
from single_course import one_course
from loguru import logger
def kill_course():
# 统计课程完成情况
cnt = defaultdict(list)
# 读取课程信息
info = json.load(open('course_info.json', 'r'))
for k, v in info.items():
if v['rate'] < 100:
cnt[v['type']].append((k,v['rate'])) # 课程类型作为键课程id作为值
print(cnt)
# 按必修-选秀-专题-培训执行刷课
must = cnt['必修']
elective = cnt['选修']
special = cnt['专题']
train = cnt['培训']
while must or elective or special or train:
if must:
course_info = must.pop()
logger.info('当前刷课序号:{}'.format(course_info[0]))
one_course(course_info[0], '必修', course_info[1])
if elective:
course_info = elective.pop()
logger.info('当前刷课序号:{}'.format(course_info[0]))
one_course(course_info[0], '选修', course_info[1])
if special:
course_info = special.pop()
logger.info('当前刷课序号:{}'.format(course_info[0]))
one_course(course_info[0], '专题', course_info[1])
if train:
course_info = train.pop()
logger.info('当前刷课序号:{}'.format(course_info[0]))
one_course(course_info[0], '培训', course_info[1])
if __name__ == '__main__':
kill_course()

@ -0,0 +1,5 @@
# 实现答题和评分
'''
检测课程状态是否存在未评价或者未答题状态
'''
from get_info import get_info

@ -0,0 +1,6 @@
from get_info import get_info
# 获取课程信息
get_info()
# 执行刷课
from kill_course import kill_course
kill_course()

@ -1,15 +1,21 @@
from DrissionPage import ChromiumPage
from DrissionPage.common import *
import time
from loguru import logger
# 实现单个课程的刷课逻辑
'''
接受一个课程id,执行刷课
1. 跳转到对应课程页面
2. 进入相应的课程界面
3. 循环检验课程是否完成
4. 完成当前课程后退出
'''
def one_course(cid:str,ctype:str,crate:int):
'''
cid: 课程id
ctype: 课程类型
crate: 课程总体完成率
----------------------
接受一个课程id,执行刷课
1. 跳转到对应课程页面
2. 进入相应的课程界面
3. 循环检验课程是否完成
4. 完成当前课程后退出
'''
# 接管浏览器
cur_page = ChromiumPage()
# 判定课程类别
@ -45,25 +51,37 @@ def one_course(cid:str,ctype:str,crate:int):
<div id="normalModel_nodeList" style="max-height:550px;overflow:auto;">
<div class="item-title selected" id="node_27817"><div class="courseName" title="活的马克思主义(上)" style="font-size:13px;">活的马克思主义()</div><span class="progressBar progress" style="font-size:12px;" id="progress_27817"><img id="progress_27817_percentImage" src="/js/axsProgress/images/percentImage.png" alt="100" style="width: 70px;height:12px;background-position: -50px 50%;background-image: url(/js/axsProgress/images/percentImage_back1.png);padding: 0;margin: 0;" class="percentImage" title="100%"> <span id="progress_27817_percentText" class="percentText" style="font-size:13px;">100%</span></span></div><div class="item-title" id="node_27821"><div class="courseName" title="活的马克思主义(下)" style="font-size:13px;">活的马克思主义()</div><span class="progressBar progress" style="font-size:12px;" id="progress_27821"><img id="progress_27821_percentImage" src="/js/axsProgress/images/percentImage.png" alt="100" style="width: 70px;height:12px;background-position: -50px 50%;background-image: url(/js/axsProgress/images/percentImage_back1.png);padding: 0;margin: 0;" class="percentImage" title="100%"> <span id="progress_27821_percentText" class="percentText" style="font-size:13px;">100%</span></span></div></div>
'''
l = []
while 1:
# todo:想要加一个定时刷新的功能方便释放内存
watch_rates = tab.ele('#normalModel_nodeList').eles('tag:span')
l = []
for watch_rate in watch_rates:
l.append(int(watch_rate.text[:-1]))
# 取偶数索引
l = l[::2]
n = len(l)
infol = [str(i)+'%' for i in l]
logger.info('当前各个视频完成率:{}'.format(infol))
if l == [100]*len(l):
# 关闭当前标签页
logger.warning('当前课程已完成,当前标签页即将关闭')
time.sleep(1)
cur_page.close_tabs(tabs_or_ids=[tab])
break
'''
由于视频播完后会自动暂停所以需要检测是否播放完毕以准备下一步操作
解决方案检测列表中首个不是0的元素的索引根据索引点击对应的视频并尝试检测是否有播放按钮来区分正在播放和播放完毕
解决方案:检测列表中首个不是100的元素的索引,根据索引点击对应的视频,并尝试检测是否有播放按钮来区分正在播放和播放完毕
'''
# 睡觉了,明天继续:)
for i in l:
if i != 100:
tab.ele('#normalModel_nodeList').eles('tag:div')[l.index(i)].click()
time.sleep(1)
try:
if tab.ele('c:#normalModel_video > xg-start > div.xgplayer-icon-play > svg > path',timeout=2):
tab.ele('c:#normalModel_video > xg-start > div.xgplayer-icon-play > svg > path').click()
except:
pass
time.sleep(60) # 每次监测间隔60秒
break
break # なぜここにbreakがいるのですかあかしいなあ。
if __name__ == '__main__':
one_course('6992','培训',68)
Loading…
Cancel
Save