From 29dc7b5562abf2cc0b29f77c17857d917ed99c2b Mon Sep 17 00:00:00 2001
From: JesterHey <144512889+JesterHey@users.noreply.github.com>
Date: Thu, 7 Dec 2023 14:42:32 +0800
Subject: [PATCH] Add files via upload
---
AES.py | 45 +++
cloud.py | 4 +-
demo.py | 24 +-
demo2.py | 232 ++++++++------
get_params.py | 283 ++++++++++--------
login_ui.py | 259 +++++++++-------
main.py | 99 +++++-
show_welcome.py | 85 +++---
...嗨,无任何不良引导和倾向).md | 2 +-
9 files changed, 613 insertions(+), 420 deletions(-)
create mode 100644 AES.py
diff --git a/AES.py b/AES.py
new file mode 100644
index 0000000..1da347d
--- /dev/null
+++ b/AES.py
@@ -0,0 +1,45 @@
+from Crypto.Cipher import AES
+from Crypto.Random import get_random_bytes
+from Crypto.Util.Padding import pad, unpad
+
+def encrypt_message(message, key):
+ """
+ 加密函数
+ :param message: 要加密的明文消息
+ :param key: 加密密钥
+ :return: 密文
+ """
+ # 创建 AES 对象
+ cipher = AES.new(key, AES.MODE_CBC)
+
+ # 对明文进行填充,然后加密
+ ct_bytes = cipher.encrypt(pad(message.encode(), AES.block_size))
+
+ # 将 IV 和密文返回
+ return cipher.iv, ct_bytes
+
+def decrypt_message(iv, ct, key):
+ """
+ 解密函数
+ :param iv: 初始化向量
+ :param ct: 密文
+ :param key: 加密密钥
+ :return: 解密后的明文
+ """
+ # 创建一个新的 AES 对象
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+
+ # 解密并去除填充
+ pt = unpad(cipher.decrypt(ct), AES.block_size).decode()
+
+ return pt
+
+# 测试
+key = get_random_bytes(16) # 生成随机密钥
+message = "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"
+
+iv, ct = encrypt_message(message, key)
+print("Encrypted:", ct)
+
+pt = decrypt_message(iv, ct, key)
+print("Decrypted:", pt)
diff --git a/cloud.py b/cloud.py
index 848a964..86a534b 100644
--- a/cloud.py
+++ b/cloud.py
@@ -32,4 +32,6 @@ def upload(name):
bucket.put_object_from_file(name, name)
def delete(name):
- bucket.delete_object(name)
\ No newline at end of file
+ bucket.delete_object(name)
+
+delete('18503.json')
\ No newline at end of file
diff --git a/demo.py b/demo.py
index 824cd4f..41b05df 100644
--- a/demo.py
+++ b/demo.py
@@ -1,19 +1,5 @@
-from openai import OpenAI
-import openai
-import os
-openai.api_base = "https://api.openai-proxy.com"
-openai.api_key = "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"
-# os.environ['http_proxy'] = 'http://127.0.0.1:10809'
-# os.environ['https_proxy'] = 'http://127.0.0.1:10809'
-client = OpenAI(
- api_key='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',
- base_url='https://api.openai-proxy.com'
-)
-
-resp = client.chat.completions.create(
- model='gpt-3.5-turbo',
- messages=[
- {'role': 'user', 'content': '你好!'}
- ]
-)
-print(resp.choices[0].message.content)
\ No newline at end of file
+import json
+with open('demo.json','r',encoding='utf-8') as f:
+ data = json.load(f)
+for i,j in enumerate(data.items()):
+ print(i,j[-1])
\ No newline at end of file
diff --git a/demo2.py b/demo2.py
index 924a8db..cada1eb 100644
--- a/demo2.py
+++ b/demo2.py
@@ -1,98 +1,134 @@
-import sys
-from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap, QPainter
-
-class MyApp(QWidget):
- def __init__(self):
- super().__init__()
- self.initUI()
-
- def initUI(self):
- # 加载背景图片
- self.background = QPixmap('hnu.jpg')
-
- # 创建一个垂直布局
- mainLayout = QVBoxLayout()
-
- # 创建三个文本框并设置占位符
- self.nameEdit = QLineEdit()
- self.pwdEdit = QLineEdit()
- self.urlEdit = QLineEdit()
- self.nameEdit.setPlaceholderText('Name')
- self.pwdEdit.setPlaceholderText('Password')
- self.urlEdit.setPlaceholderText('URL')
-
- # 创建一个按钮并连接信号
- btn = QPushButton('提交')
- btn.clicked.connect(self.onSubmit)
-
- # 对于每个输入框,创建一个水平布局以保持居中
- for label_text, edit_widget in [("用户名", self.nameEdit),
- ("密码", self.pwdEdit),
- ("实训网址", self.urlEdit)]:
- hbox = QHBoxLayout()
- hbox.addStretch()
- vbox_inner = QVBoxLayout()
- vbox_inner.addWidget(QLabel(label_text))
- vbox_inner.addWidget(edit_widget)
- hbox.addLayout(vbox_inner)
- hbox.addStretch()
- mainLayout.addLayout(hbox)
-
- # 添加按钮到布局
- mainLayout.addWidget(btn, 0, Qt.AlignCenter)
-
- # 设置窗口的布局
- self.setLayout(mainLayout)
- self.setWindowTitle('')
- self.setGeometry(300, 300, 400, 300)
- self.centerWindow()
-
- # 应用样式
- self.applyStyles()
-
- def paintEvent(self, event):
- painter = QPainter(self)
-
- # 计算背景图片绘制的起始坐标,使其位于窗口中心
- bgWidth = self.background.width()
- bgHeight = self.background.height()
- startX = (self.width() - bgWidth) // 2
- startY = (self.height() - bgHeight) // 2
-
- painter.drawPixmap(startX, startY, self.background)
-
- def centerWindow(self):
- qr = self.frameGeometry()
- cp = QApplication.desktop().availableGeometry().center()
- qr.moveCenter(cp)
- self.move(qr.topLeft())
-
- def applyStyles(self):
- self.setStyleSheet("""
- QLineEdit {
- border: 1px solid gray;
- border-radius: 10px;
- padding: 5px;
- background: transparent;
- }
- QLineEdit:focus {
- border: 1px solid LightSeaGreen;
- }
- """)
-
- def onSubmit(self):
- name = self.nameEdit.text()
- pwd = self.pwdEdit.text()
- url = self.urlEdit.text()
- print(f"Name: {name}, Password: {pwd}, URL: {url}")
-
- # 关闭窗口
- self.close()
-
-if __name__ == '__main__':
- app = QApplication(sys.argv)
- ex = MyApp()
- ex.show()
- sys.exit(app.exec_())
+'''
+本模块用于创建登录界面并保存用户信息
+'''
+import sys
+from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel
+from PyQt5.QtCore import Qt,QTimer
+from PyQt5.QtGui import QPixmap, QPainter
+import os
+import platform
+import json
+
+class MyApp(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.initUI()
+
+ def initUI(self):
+ # 加载背景图片
+ self.background = QPixmap()
+ # 创建一个垂直布局
+ mainLayout = QVBoxLayout()
+
+ # 创建三个文本框并设置占位符
+ self.nameEdit = QLineEdit()
+ self.pwdEdit = QLineEdit()
+ self.urlEdit = QLineEdit()
+
+ # 创建一个按钮并连接信号
+ btn = QPushButton('提交')
+ btn.clicked.connect(self.onSubmit)
+
+ # 创建一个用于显示错误信息的标签
+ self.errorLabel = QLabel('', self)
+ self.errorLabel.hide() # 初始时隐藏该标签
+
+ self.setWindowTitle('头歌助手登录')
+
+ # 对于每个输入框,创建一个水平布局以保持居中
+ for label_text, edit_widget in [("用户名", self.nameEdit),
+ ("密 码", self.pwdEdit),
+ ("实训网址", self.urlEdit)]:
+ hbox = QHBoxLayout()
+ hbox.addStretch()
+ vbox_inner = QVBoxLayout()
+ vbox_inner.addWidget(QLabel(label_text))
+ vbox_inner.addWidget(edit_widget)
+ hbox.addLayout(vbox_inner)
+ hbox.addStretch()
+ mainLayout.addLayout(hbox)
+
+ # 添加按钮和错误信息标签到布局
+ mainLayout.addWidget(btn, 0, Qt.AlignCenter)
+ mainLayout.addWidget(self.errorLabel, 0, Qt.AlignCenter)
+
+ # 设置窗口的布局
+ self.setLayout(mainLayout)
+ self.setGeometry(300, 300, 650, 550)
+ self.centerWindow()
+
+ # 应用样式
+ self.applyStyles()
+
+ def paintEvent(self, event):
+ painter = QPainter(self)
+ painter.drawPixmap(self.rect(), self.background)
+
+ def centerWindow(self):
+ qr = self.frameGeometry()
+ cp = QApplication.desktop().availableGeometry().center()
+ qr.moveCenter(cp)
+ self.move(qr.topLeft())
+
+ def applyStyles(self):
+ self.setStyleSheet("""
+ QLineEdit {
+ border: 1px solid gray;
+ border-radius: 10px;
+ padding: 5px;
+ background: rgba(255, 255, 255, 100);
+ color: black;
+ width: 300px;
+ height: 30px;
+ }
+ QLineEdit:focus {
+ border: 3px solid black;
+ }
+ QLabel {
+ color: black;
+ font-size: 20pt;
+ font-family: "Microsoft YaHei";
+ }
+ QPushButton {
+ border: 1px solid gray;
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 100);
+ color: black;
+ width: 100px;
+ height: 27px;
+ }
+ /* 样式用于错误信息标签 */
+ QLabel#errorLabel {
+ color: red;
+ font-size: 16pt;
+ }
+ """)
+
+ def onSubmit(self):
+ name = self.nameEdit.text()
+ pwd = self.pwdEdit.text()
+ url = self.urlEdit.text()
+
+ if name == '' or pwd == '' or url == '':
+ self.showError("非法输入!")
+ else:
+ with open('userinfo.json', 'w') as f:
+ json.dump({'name': name, 'pwd': pwd, 'url': url}, f)
+ self.close()
+
+ def showError(self, message):
+ self.errorLabel.setText(message)
+ self.errorLabel.show()
+ QTimer.singleShot(2000, self.hideError)
+
+ def hideError(self):
+ self.errorLabel.hide()
+
+def main():
+ app = QApplication(sys.argv)
+ ex = MyApp()
+ ex.show()
+ sys.exit(app.exec_())
+
+if __name__ == '__main__':
+ main()
diff --git a/get_params.py b/get_params.py
index 2b990bf..e2f1528 100644
--- a/get_params.py
+++ b/get_params.py
@@ -10,6 +10,7 @@
'''
#导入所需模块
+import platform
import os
import json
import time
@@ -25,147 +26,167 @@ from cloud import is_exist,download
#配置参数
opt = Options()
opt.add_experimental_option('detach', True)
-opt.add_argument('--headless')
+#opt.add_argument('--headless')
chrome_driver = 'D:\ChromeDownload\chromedriver-win64\chromedriver-win64'
-#以下部分在发行版本需要优化UI
-# url = input('请输入作业中任意一关的网址:')
-# user_name = input('请输入用户名:')
-# password = input('请输入密码:')
+# 发行版时改为调用userinfo.json文件中的用户名和密码
url = 'https://www.educoder.net/tasks/27V4D95N/1191515/vmxpzae734bj?coursesId=27V4D95N'
user_name = 'hnu202311020126'
password = 'hzy123456'
-# 另外,目前好像只有实训作业有这些参数,其他的作业例如编程作业就没有,所以先判断一下是否为实训作业,可以通过用户输入的url判断
-# 主要是看educoder.net/后面是否有tasks,如果有,则是实训作业,否则,不是实训作业
+platf = platform.platform()
def is_practice(url:str) -> bool:
obj=re.compile(r'www.educoder.net/tasks')
if obj.search(url):
return True
else:
return False
-if is_practice(url):
- #构造selenium对象
- safari = Chrome(options=opt)
- safari.get(url)
- #模拟登录
- safari.implicitly_wait(10)
- safari.find_element(By.ID, 'login').send_keys(user_name)
- safari.find_element(By.ID, 'password').send_keys(password)
- safari.find_element(By.ID, 'password').send_keys(Keys.ENTER)
- time.sleep(2)
- #获取cookie,User-Agent
- Cookie = safari.get_cookies()
- User_Agent = safari.execute_script('return navigator.userAgent')
- cookie = f'autologin_trustie={Cookie[1]["value"]}; _educoder_session={Cookie[0]["value"]}'
- #先获取到shixun_id便于先判断云端文件是否存在
- cur_url = url
- identity = cur_url.split('/')[-1].split('?')[0]
- id_url = f'https://data.educoder.net/api/tasks/{identity}.json?'
- headers = {
- 'Cookie':cookie,
- 'User-Agent':User_Agent,
- 'Referer':cur_url
- }
- response = requests.get(url=id_url, headers=headers)
- shixun_id = dict(response.json())['challenge']['shixun_id']
- #判断云端文件是否存在
- try:
- exist = is_exist(f'{shixun_id}.json')
- if exist: #存在,则跳转到云端下载并终止本程序
- print('云端文件已存在,正在下载')
- download(f'{shixun_id}.json')
- # 检测本地文件是否下载完成
- while True:
- try:
- if os.path.exists(f'{shixun_id}.json'):
- print('下载完成')
- safari.quit()
- exit()
- break
- except Exception as e:
- print(e)
- except Exception as e:
- print(e)
- finally: #不存在,则继续执行本程序
- #获取关卡数
- #点击展开关卡页面
- safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[1]/a[1]').click()
+# 另外,目前好像只有实训作业有这些参数,其他的作业例如编程作业就没有,所以先判断一下是否为实训作业,可以通过用户输入的url判断
+# 主要是看educoder.net/后面是否有tasks,如果有,则是实训作业,否则,不是实训作业
+#为方便main.py调用,将判断函数写入函数中,以下部分封装为函数
+def get_parameters(url:str,user_name:str,password:str):
+ url = url
+ user_name = user_name
+ password = password
+ if is_practice(url):
+ #构造selenium对象
+ safari = Chrome(options=opt)
+ safari.get(url)
+ #模拟登录
+ safari.implicitly_wait(10)
+ safari.find_element(By.ID, 'login').send_keys(user_name)
+ safari.find_element(By.ID, 'password').send_keys(password)
+ safari.find_element(By.ID, 'password').send_keys(Keys.ENTER)
time.sleep(2)
- #关卡数量由 class = "flex-container challenge-title space-between" 的元素数量决定
- htmltxt = safari.page_source
- html = etree.HTML(htmltxt)
- task_num = html.xpath('count(//*[@class="flex-container challenge-title space-between"])')
- task_num = int(task_num)
- #关闭关卡页面
- safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[3]/div[1]').click()
- #对于每一关,获取参数
- #每一关的参数由以下元素组成:
- '''
- /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[3]/div[1]/a
- /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[4]/div[1]/a
- '''
- obj1 = re.compile(r'
任务描述
(?P.*?)
',re.S)
- obj2 = re.compile(r'编程要求
(?P.*?)
',re.S)
- #初始化一个字典,用于存放所有关卡的参数
- total = {}
- i=1
+ #判断是否登录成功
try:
- while i <= task_num:
- safari.implicitly_wait(10)
- safari.find_element(By.XPATH, '//*[@id="task-left-panel"]/div[1]/a[1]').click()
- safari.implicitly_wait(10)
- safari.find_element(By.XPATH,f'/html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[{i}]/div[1]/a').click()
- time.sleep(3)
- #获取课程id -> 根据url中?前面的,最后一个/后面的那部分参数构造请求,同时,似乎还需要用到cookie,User-Agent和Referer参数,这些统一用selenium在登陆后获取并组装成headers
- #获取cookie,User-Agent和Referer
- cur_url=Referer = safari.current_url
- identity = cur_url.split('/')[-1].split('?')[0]
- id_url = f'https://data.educoder.net/api/tasks/{identity}.json?'
- #获取课程id
- headers = {
- 'Cookie':cookie,
- 'User-Agent':User_Agent,
- 'Referer':Referer
- }
- try:
- response = requests.get(url=id_url,headers=headers)
- challenge_id = dict(response.json())['challenge']['id']
- shixun_id = dict(response.json())['challenge']['shixun_id']
- except BaseException:
- print('获取课程id失败')
- #获取任务描述(如果存在的话)
- page_source = safari.page_source
- describe = obj1.findall(page_source)
- #获取编程要求(如果存在的话)
- require = obj2.findall(page_source)
- #获取编辑器中的代码,由于代码都是class = "view-line"的div,先找到所有class = "view-line"的div,获取其中的所有文本,再把不同行的代码用\n连接起来
- code = safari.find_elements(By.CLASS_NAME,'view-line')
- code = '\n'.join([i.text for i in code]).lstrip('\n')
- #把参数存入字典,再转换为json格式
- task = {
- 'describe':describe[0] if len(describe) != 0 else '',
- 'require':require[0] if len(require) != 0 else '',
- 'code':code,
- 'verified': False, #这个参数是用来标记答案是否被用户认证为正确答案的,初始值为False
- 'last_modified': time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) #这个参数是用来标记答案最后一次被修改的时间,初始值为当前时间
- }
- #把每一关的参数存入总的字典中
- total[challenge_id] = task
- #去往下一关
- i += 1
+ safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[1]/a[1]')
except BaseException:
- print(f'{challenge_id}参数获取参数失败')
- #判断爬取到的代码是否存在空值,如果存在空值,则重新爬取
- for value in total.values():
- if value['code'] == '':
- print('检测到代码参数为空值,重新爬取')
- safari.close()
- os.system('python get_params.py')
- exit()
- #把参数写入本地json文件中,文件名字与shixun_name相同键为course_id,值为一个列表,列表中每个元素为一个字典,字典中包含每一关的参数
- with open(f'{shixun_id}.json','w',encoding='utf-8') as f:
- json.dump(total,f,ensure_ascii=False,indent=4)
- #关闭浏览器
- safari.quit()
-else:
- print('这不是一个实训作业')
- exit()
+ print('登录失败 请检查输入信息是否正确')
+ # 关闭浏览器
+ safari.quit()
+ #重新调用login_ui.py
+ if 'Windows' in platf:
+ os.system('python login_ui.py')
+ else:
+ os.system('python3 login_ui.py')
+ #获取cookie,User-Agent
+ Cookie = safari.get_cookies()
+ User_Agent = safari.execute_script('return navigator.userAgent')
+ cookie = f'autologin_trustie={Cookie[1]["value"]}; _educoder_session={Cookie[0]["value"]}'
+ #先获取到shixun_id便于先判断云端文件是否存在
+ cur_url = url
+ identity = cur_url.split('/')[-1].split('?')[0]
+ id_url = f'https://data.educoder.net/api/tasks/{identity}.json?'
+ headers = {
+ 'Cookie':cookie,
+ 'User-Agent':User_Agent,
+ 'Referer':cur_url
+ }
+ response = requests.get(url=id_url, headers=headers)
+ shixun_id = dict(response.json())['challenge']['shixun_id']
+ #判断云端文件是否存在
+ try:
+ exist = is_exist(f'{shixun_id}.json')
+ if exist: #存在,则跳转到云端下载并终止本程序
+ print('云端文件已存在,正在下载')
+ download(f'{shixun_id}.json')
+ # 检测本地文件是否下载完成
+ while True:
+ try:
+ if os.path.exists(f'{shixun_id}.json'):
+ print('下载完成')
+ safari.quit()
+ exit()
+ break
+ except Exception as e:
+ print(e)
+ except Exception as e:
+ print(e)
+ finally: #不存在,则继续执行本程序
+ print('云端文件不存在,正在爬取')
+ #获取关卡数
+ #点击展开关卡页面
+ safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[1]/a[1]').click()
+ time.sleep(2)
+ #关卡数量由 class = "flex-container challenge-title space-between" 的元素数量决定
+ htmltxt = safari.page_source
+ html = etree.HTML(htmltxt)
+ task_num = html.xpath('count(//*[@class="flex-container challenge-title space-between"])')
+ task_num = int(task_num)
+ #关闭关卡页面
+ safari.find_element(By.XPATH,'//*[@id="task-left-panel"]/div[3]/div[1]').click()
+ #对于每一关,获取参数
+ #每一关的参数由以下元素组成:
+ '''
+ /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[3]/div[1]/a
+ /html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[4]/div[1]/a
+ '''
+ obj1 = re.compile(r'任务描述
(?P.*?)
',re.S)
+ obj2 = re.compile(r'编程要求
(?P.*?)
',re.S)
+ #初始化一个字典,用于存放所有关卡的参数
+ total = {}
+ i=1
+ try:
+ while i <= task_num:
+ safari.implicitly_wait(10)
+ safari.find_element(By.XPATH, '//*[@id="task-left-panel"]/div[1]/a[1]').click()
+ safari.implicitly_wait(10)
+ safari.find_element(By.XPATH,f'/html/body/div[1]/div/div/div/div[2]/section[1]/div[3]/div[3]/div/div/div/div/div[{i}]/div[1]/a').click()
+ time.sleep(3)
+ #获取课程id -> 根据url中?前面的,最后一个/后面的那部分参数构造请求,同时,似乎还需要用到cookie,User-Agent和Referer参数,这些统一用selenium在登陆后获取并组装成headers
+ #获取cookie,User-Agent和Referer
+ cur_url=Referer = safari.current_url
+ identity = cur_url.split('/')[-1].split('?')[0]
+ id_url = f'https://data.educoder.net/api/tasks/{identity}.json?'
+ #获取课程id
+ headers = {
+ 'Cookie':cookie,
+ 'User-Agent':User_Agent,
+ 'Referer':Referer
+ }
+ try:
+ response = requests.get(url=id_url,headers=headers)
+ challenge_id = dict(response.json())['challenge']['id']
+ shixun_id = dict(response.json())['challenge']['shixun_id']
+ except BaseException:
+ print('获取课程id失败')
+ #获取任务描述(如果存在的话)
+ page_source = safari.page_source
+ describe = obj1.findall(page_source)
+ #获取编程要求(如果存在的话)
+ require = obj2.findall(page_source)
+ #获取编辑器中的代码,由于代码都是class = "view-line"的div,先找到所有class = "view-line"的div,获取其中的所有文本,再把不同行的代码用\n连接起来
+ code = safari.find_elements(By.CLASS_NAME,'view-line')
+ code = '\n'.join([i.text for i in code]).lstrip('\n')
+ #把参数存入字典,再转换为json格式
+ task = {
+ 'describe':describe[0] if len(describe) != 0 else '',
+ 'require':require[0] if len(require) != 0 else '',
+ 'code':code,
+ 'verified': False, #这个参数是用来标记答案是否被用户认证为正确答案的,初始值为False
+ 'last_modified': time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())) #这个参数是用来标记答案最后一次被修改的时间,初始值为当前时间
+ }
+ #把每一关的参数存入总的字典中
+ total[challenge_id] = task
+ #去往下一关
+ i += 1
+ except BaseException:
+ print(f'{challenge_id}参数获取参数失败')
+ #判断爬取到的代码是否存在空值,如果存在空值,则重新爬取
+ for value in total.values():
+ if value['code'] == '':
+ print('检测到代码参数为空值,重新爬取')
+ safari.close()
+ # 再次执行本程序
+ if 'Windows' in platf:
+ os.system('python get_params.py')
+ else:
+ os.system('python3 get_params.py')
+ #把参数写入本地json文件中,文件名字与shixun_name相同键为course_id,值为一个列表,列表中每个元素为一个字典,字典中包含每一关的参数
+ with open(f'{shixun_id}.json','w',encoding='utf-8') as f:
+ json.dump(total,f,ensure_ascii=False,indent=4)
+ print('参数爬取完成')
+ #关闭浏览器
+ safari.quit()
+ else:
+ print('这不是一个实训作业')
+ exit()
diff --git a/login_ui.py b/login_ui.py
index 5a5508a..fa07d82 100644
--- a/login_ui.py
+++ b/login_ui.py
@@ -1,116 +1,143 @@
-import sys
-from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel
-from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QPixmap, QPainter
-import os
-class MyApp(QWidget):
- def __init__(self):
- super().__init__()
- self.initUI()
-
- def initUI(self):
- # 加载背景图片
- self.background = QPixmap('b3o.png')
- # 创建一个垂直布局
- mainLayout = QVBoxLayout()
-
- # 创建三个文本框并设置占位符
- self.nameEdit = QLineEdit()
- self.pwdEdit = QLineEdit()
- self.urlEdit = QLineEdit()
- # self.nameEdit.setPlaceholderText('Name')
- # self.pwdEdit.setPlaceholderText('Password')
- # self.urlEdit.setPlaceholderText('URL')
-
- # 创建一个按钮并连接信号
- btn = QPushButton('提交')
- btn.clicked.connect(self.onSubmit)
-
- self.setWindowTitle('头歌助手登录')
- # 对于每个输入框,创建一个水平布局以保持居中
- for label_text, edit_widget in [("用户名", self.nameEdit),
- ("密 码", self.pwdEdit),
- ("实训网址", self.urlEdit)]:
- hbox = QHBoxLayout() #创建一个水平布局
- hbox.addStretch() #添加一个伸缩因子,使得文本框和按钮位于窗口中心
- vbox_inner = QVBoxLayout() #创建一个垂直布局
- vbox_inner.addWidget(QLabel(label_text)) #添加一个标签
- vbox_inner.addWidget(edit_widget) #添加一个文本框
- hbox.addLayout(vbox_inner) #将垂直布局添加到水平布局中
- hbox.addStretch() #添加一个伸缩因子,使得文本框和按钮位于窗口中心
- mainLayout.addLayout(hbox) #将水平布局添加到垂直布局中
-
- # 添加按钮到布局
- mainLayout.addWidget(btn, 0, Qt.AlignCenter)
-
- # 设置窗口的布局
- self.setLayout(mainLayout)
- self.setWindowTitle('')
- self.setGeometry(300, 300, 650,550) # 设置窗口大小,其中300,300为窗口左上角坐标,1000,600为窗口大小
- self.centerWindow()
-
- # 应用样式
- self.applyStyles()
- self.show()
-
- def paintEvent(self, event):
- painter = QPainter(self)
- painter.drawPixmap(self.rect(), self.background)
-
- def centerWindow(self):
- qr = self.frameGeometry()
- cp = QApplication.desktop().availableGeometry().center()
- qr.moveCenter(cp)
- self.move(qr.topLeft())
-
- def applyStyles(self):
- # css 用于设置窗口样式
- self.setStyleSheet("""
- QLineEdit {
- border: 1px solid gray;
- border-radius: 10px;
- padding: 5px;
- background: rgba(255, 255, 255, 100);
- }
- QLineEdit:focus {
- border: 1px solid black;
- }
- QLabel {
- color: black;
- font-size: 23pt;
- margin-top: 50px; /* 顶部外边距 */
- margin-left: 20px; /* 左侧外边距 */
- }
- QLineEdit {
- border: 1px solid gray;
- border-radius: 10px;
- padding: 5px;
- background: rgba(255, 255, 255, 100);
- color: black; /* 设置输入文本颜色为黑色 */
- width: 300px; /* 设置输入框宽度 */
- height: 30px; /* 设置输入框高度 */
- }
- QPushButton {
- border: 1px solid gray;
- border-radius: 10px;
- padding: 5px;
- background: rgba(255, 255, 255, 100);
- color: black; /* 设置按钮文本颜色为黑色 */
- width: 100px; /* 设置按钮宽度 */
- height: 27px; /* 设置按钮高度 */
- }
- """)
-
- def onSubmit(self):
- name = self.nameEdit.text()
- pwd = self.pwdEdit.text()
- url = self.urlEdit.text()
- print(f"Name: {name}, Password: {pwd}, URL: {url}")
-
- # 关闭窗口
- self.close()
-os.system('python3 show_welcome.py')
-app = QApplication(sys.argv)
-ex = MyApp()
-ex.show()
-sys.exit(app.exec_())
\ No newline at end of file
+'''
+本模块用于创建登录界面并保存用户信息
+'''
+import sys
+from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QLabel
+from PyQt5.QtCore import Qt,QTimer
+from PyQt5.QtGui import QPixmap, QPainter
+import os
+import platform
+import json
+
+class MyApp(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.initUI()
+
+ def initUI(self):
+ # 加载背景图片
+ self.background = QPixmap('picture\\b2.png')
+ # 创建一个垂直布局
+ mainLayout = QVBoxLayout()
+
+ # 创建三个文本框并设置占位符
+ self.nameEdit = QLineEdit()
+ self.pwdEdit = QLineEdit()
+ self.urlEdit = QLineEdit()
+
+ # 创建一个按钮并连接信号
+ btn = QPushButton('提交')
+ btn.clicked.connect(self.onSubmit)
+
+ # 创建一个用于显示错误信息的标签
+ self.errorLabel = QLabel('', self)
+ self.errorLabel.hide() # 初始时隐藏该标签
+
+ self.setWindowTitle('头歌助手登录')
+
+ # 对于每个输入框,创建一个水平布局以保持居中
+ for label_text, edit_widget in [("用户名", self.nameEdit),
+ ("密 码", self.pwdEdit),
+ ("实训网址", self.urlEdit)]:
+ hbox = QHBoxLayout()
+ hbox.addStretch()
+ vbox_inner = QVBoxLayout()
+ vbox_inner.addWidget(QLabel(label_text))
+ vbox_inner.addWidget(edit_widget)
+ hbox.addLayout(vbox_inner)
+ hbox.addStretch()
+ mainLayout.addLayout(hbox)
+
+ # 添加按钮和错误信息标签到布局
+ mainLayout.addWidget(btn, 0, Qt.AlignCenter)
+ mainLayout.addWidget(self.errorLabel, 0, Qt.AlignCenter)
+
+ # 设置窗口的布局
+ self.setLayout(mainLayout)
+ self.setGeometry(350, 350, 1200, 700)
+ self.centerWindow()
+
+ # 应用样式
+ self.applyStyles()
+
+ def paintEvent(self, event):
+ painter = QPainter(self)
+ painter.drawPixmap(self.rect(), self.background)
+
+ def centerWindow(self):
+ qr = self.frameGeometry()
+ cp = QApplication.desktop().availableGeometry().center()
+ qr.moveCenter(cp)
+ self.move(qr.topLeft())
+
+ def applyStyles(self):
+ self.setStyleSheet("""
+ QLineEdit {
+ border: 1px solid gray;
+ border-radius: 10px;
+ padding: 5px;
+ background: rgba(255, 255, 255, 100);
+ color: black;
+ width: 300px;
+ height: 50px;
+ font-size: 14pt; /* 设置字号为14磅 */
+ font-family: 'Microsoft YaHei'; /* 设置字体为微软雅黑 */
+ }
+ QLineEdit:focus {
+ border: 3px solid black;
+ }
+ QLabel {
+ color: black;
+ font-size: 20pt;
+ font-family: "Microsoft YaHei";
+ margin-top: 50px; /* 顶部外边距 */
+ margin-left: 200px; /* 左侧外边距 */
+ }
+ QPushButton {
+ border: 1px solid gray;
+ border-radius: 10px;
+ background: rgba(255, 255, 255, 100);
+ color: black;
+ width: 100px;
+ height: 27px;
+ }
+ /* 样式用于错误信息标签 */
+ QLabel#errorLabel {
+ color: red;
+ font-size: 16pt;
+ }
+ """)
+
+ def onSubmit(self):
+ name = self.nameEdit.text()
+ pwd = self.pwdEdit.text()
+ url = self.urlEdit.text()
+
+ if name == '' or pwd == '' or url == '' or ~url.startswith('https') or ~url.startswith('www'):
+ self.showError("非法输入!")
+ else:
+ with open('userinfo.json', 'w') as f:
+ json.dump({'name': name, 'pwd': pwd, 'url': url}, f)
+ self.close()
+
+ def showError(self, message):
+ self.errorLabel.setText(message)
+ self.errorLabel.show()
+ QTimer.singleShot(1200, self.hideError)
+
+ def hideError(self):
+ self.errorLabel.hide()
+ # 关闭窗口
+
+
+
+# 判断当前操作系统(基础太渣,只能这样判断了,哈哈)
+platf = platform.platform()
+if platf.startswith('Windows'):
+ os.system('python show_welcome.py')
+else:
+ os.system('python3 show_welcome.py')
+app = QApplication(sys.argv) # 创建应用程序对象
+ex = MyApp() # 创建窗口对象
+ex.show() # 显示窗口
+sys.exit(app.exec_()) # 保证程序完整退出
\ No newline at end of file
diff --git a/main.py b/main.py
index 886f5bd..5958e92 100644
--- a/main.py
+++ b/main.py
@@ -1,15 +1,84 @@
-'''
-主程序:整合各个模块
-1、ui文件调用相应ui模块
-2、get_params.py获取参数
-3、get_answer.py获取答案
-4、cloud.py将json文件存入云端
-'''
-
-# 生成图形化界面,引导用户登陆并输入实训网址
-# 调用get_params.py获取参数,这一步同时隐含了云端获取答案的过程
-# 如果云端答案不存在,则调用get_answer.py获取答案,并展示给用户
-# 用户verif选项确认后,调用cloud.py将json文件存入云端
-
-
-import login_ui
\ No newline at end of file
+'''
+主程序:整合各个模块
+1、ui文件调用相应ui模块
+2、get_params.py获取参数
+3、get_answer.py获取答案
+4、cloud.py将json文件存入云端
+'''
+
+# 生成图形化界面,引导用户登陆并输入实训网址
+# 调用get_params.py获取参数,这一步同时隐含了云端获取答案的过程
+# 如果云端答案不存在,则调用get_answer.py获取答案,并展示给用户
+# 用户verif选项确认后,调用cloud.py将json文件存入云端
+
+# 导入所需模块
+import platform
+from get_params import get_parameters
+from get_answer import get_answer_from_api,promot,client
+import json
+import os
+
+# 判断当前操作系统
+platf = platform.platform()
+# 调用login_ui获得用户输入的用户名、密码和实训网址
+if platf.startswith('Windows'):
+ os.system('python login_ui.py')
+else:
+ os.system('python3 login_ui.py')
+
+# 检查userinfo.json文件是否存在,存在,则程序继续
+assert os.path.exists('userinfo.json'), 'userinfo.json文件不存在,请检查'
+
+# 先读取userinfo.json文件,获得用户名、密码和实训网址
+with open('userinfo.json', 'r') as f:
+ userinfo = json.load(f)
+user_name = userinfo['name']
+password = userinfo['pwd']
+url = userinfo['url']
+
+# 调用get_params.py获得参数,完成后本地应该有一个json文件,里面有参数
+get_parameters(url,user_name,password)
+def get_json(file):
+ return [i for i in os.listdir(file) if i.endswith('.json')]
+
+#将file指定为当前目录
+file = os.getcwd()
+json_names = get_json(file)
+
+# 遍历查找以数字开头的json文件,即为获得到的json文件,可能是云端下载的,也可能是爬取的
+global j_name
+for j in json_names:
+ if j[0].isdigit():
+ # 将该文件名赋值给json_name
+ j_name = j
+ break
+
+# 判断json_name文件中是否有answer,若有提取answer并格式化或展现
+with open(j_name,'r',encoding='utf-8') as f:
+ json_data = json.load(f)
+def is_exist_answer(data:dict) -> bool:
+ for i,j in data.items():
+ if 'answer' in j.keys():
+ return True
+ return False
+if is_exist_answer(json_data):
+ pass
+else:
+ # 调用API获取答案
+ new_data = get_answer_from_api(jsonfile=json_data,client=client,promot=promot)
+ # 重写本地json文件
+ with open(j_name,'w',encoding='utf-8') as f:
+ json.dump(new_data,f,ensure_ascii=False,indent=4)
+
+# 上面的判断执行完后,本地的json文件中已经有answer了,下面实现信息展示
+# release版本要优化UI,beta版本先用print代替
+
+# 读取json文件,获得answer
+with open(j_name,'r',encoding='utf-8') as f:
+ data = json.load(f)
+# 由于python3.6之后字典是有序的,所以可以直接遍历字典,显示关卡序号和答案
+# 要将key在data中对应的索引值+1,因为索引值是从0开始的
+for i,j in enumerate(data.items()):
+ print(f'第{i+1}关的答案是:{j[-1]["answer"]}')
+
+# 询问用户是否认证答案正确(下午新开一个模块实现)
\ No newline at end of file
diff --git a/show_welcome.py b/show_welcome.py
index d232daa..0466650 100644
--- a/show_welcome.py
+++ b/show_welcome.py
@@ -1,39 +1,46 @@
-import sys
-from PyQt5.QtWidgets import QApplication, QLabel, QWidget
-from PyQt5.QtGui import QPixmap
-from PyQt5.QtCore import QTimer, Qt
-
-class ImageWindow(QWidget):
- def __init__(self, image_path):
- super().__init__()
- self.initUI(image_path)
-
- def initUI(self, image_path):
- self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
- self.setAttribute(Qt.WA_TranslucentBackground)
-
- label = QLabel(self)
- pixmap = QPixmap(image_path)
-
- # 缩放图片到期望的大小
- scaled_pixmap = pixmap.scaled(700, 400, Qt.KeepAspectRatio) # 设置图片大小为400x300,并保持纵横比
- label.setPixmap(scaled_pixmap)
- self.resize(scaled_pixmap.width(), scaled_pixmap.height())
-
- # 居中显示窗口
- qr = self.frameGeometry()
- cp = QApplication.desktop().availableGeometry().center()
- qr.moveCenter(cp)
- self.move(qr.topLeft())
-
- QTimer.singleShot(3000, self.close)
-
-def show():
- app = QApplication(sys.argv)
- ex = ImageWindow('b2.png')
- ex.show()
- sys.exit(app.exec_())
-
-if __name__ == '__main__':
- show()
-
+import sys
+from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QDesktopWidget
+from PyQt5.QtGui import QPixmap
+from PyQt5.QtCore import QTimer, Qt
+
+class ImageWindow(QWidget):
+ def __init__(self, image_path):
+ super().__init__()
+ self.initUI(image_path)
+
+ def initUI(self, image_path):
+ self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
+ self.setAttribute(Qt.WA_TranslucentBackground)
+
+ label = QLabel(self)
+ pixmap = QPixmap(image_path)
+
+ # 获取屏幕尺寸
+ screen = QDesktopWidget().screenGeometry()
+
+ # 缩放图片大小
+ # 过时警告 scaled_pixmap = pixmap.scaledint(screen.width() * 0.5, screen.height() * 0.5, Qt.KeepAspectRatio)
+ scaled_pixmap = pixmap.scaled(int(screen.width() * 0.5), int(screen.height() * 0.5), Qt.KeepAspectRatio)
+ label.setPixmap(scaled_pixmap)
+ self.resize(scaled_pixmap.width(), scaled_pixmap.height())
+
+ # 居中显示窗口
+ self.centerWindow()
+
+ QTimer.singleShot(4500, self.close)
+
+ def centerWindow(self):
+ # 居中窗口
+ qr = self.frameGeometry()
+ cp = QDesktopWidget().availableGeometry().center() # 获取屏幕中心点
+ qr.moveCenter(cp)
+ self.move(qr.topLeft())
+
+def show_image():
+ app = QApplication(sys.argv)
+ ex = ImageWindow('picture\\b2txt.png') # 图片路径
+ ex.show()
+ sys.exit(app.exec_())
+
+
+show_image()
\ No newline at end of file
diff --git a/发泄(纯属口嗨,无任何不良引导和倾向).md b/发泄(纯属口嗨,无任何不良引导和倾向).md
index 0c8b70c..075e38b 100644
--- a/发泄(纯属口嗨,无任何不良引导和倾向).md
+++ b/发泄(纯属口嗨,无任何不良引导和倾向).md
@@ -1 +1 @@
-# 我真甜蜜地服了互联网上一群傻逼到处TMD跟喷史一样到处拉史一样的垃圾信息,就TM配置个代理一帮牛马讲得TMD的天花乱坠,什么TM云函数,Cloudfare代理,Njanx反向代理真NMSL的一群人。好不容易TND有几个人直接给TM代理地址又不说明要加/v1,官方文档也TM是一堆若至写的吗,API用法更新了就TM给几个示例代码是怕GitHub存储费用太高吗?社区和和论坛也是一帮彼阳的晚意,TM跟东晋士大夫一样搁哪清谈半天NM一个给代码示例的人都没有,屈指可数的几个代码又是API1.2版本之前的,用不了一点捏。我TM找整整2天就差冲OpenAI官网的时候无意间看到CSDN上的[这篇帖子](https://blog.csdn.net/qq_36265860/article/details/130111351)。WC,什么叫清晰,什么叫对比,什么叫做程序员的自我修养,TM我那么简单的一个问题就只有这个老哥给出解答。我NM,真的,当时真的想嫁给这个哥了。我宣布CSDN就是世界第一技术论坛,那些说看CSDN是屎里淘食的人下赛季池史。以后多看书,再上网查这种技术问题我也是SB。暂时就想到那么多,哈哈。
+# 我真甜蜜地服了互联网上一群傻逼到处TMD跟喷史一样到处拉史一样的垃圾信息,就TM配置个代理一帮牛马讲得TMD的天花乱坠,什么TM云函数,Cloudfare代理,Njanx反向代理真NMSL的一群人。好不容易TND有几个人直接给TM代理地址又不说明要加/v1,官方文档也TM是一堆SB写的吗,API用法更新了就TM给几个示例代码是怕GitHub存储费用太高吗?社区和和论坛也是一帮勾石东西,TM跟东晋士大夫一样搁哪清谈半天NM一个给代码示例的人都没有,屈指可数的几个代码又是API1.2版本之前的,用不了一点捏。我TM找整整2天就差冲OpenAI官网的时候无意间看到CSDN上的[这篇帖子](https://blog.csdn.net/qq_36265860/article/details/130111351)。WC,什么叫清晰,什么叫对比,什么叫做程序员的自我修养,TM我那么简单的一个问题就只有这个老哥给出解答。我NM,真的,当时真的想嫁给这个哥了。我宣布CSDN就是世界第一技术论坛,那些说看CSDN是屎里淘食的人下赛季池史。以后多看书,再上网查这种技术问题我也是SB。暂时就想到那么多,哈哈。