first commit
commit
66d5c70ca8
@ -0,0 +1,10 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# 屏蔽图片
|
||||
.png
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
@ -0,0 +1,16 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyByteLiteralInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PyPep8Inspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N802" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyRedundantParenthesesInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PyShadowingNamesInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.11" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/AnQuanWeiBan.iml" filepath="$PROJECT_DIR$/.idea/AnQuanWeiBan.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,87 @@
|
||||
import cv2
|
||||
import requests
|
||||
import os
|
||||
import json
|
||||
|
||||
# 全局变量,用来存储点击的坐标和次数
|
||||
clicks = []
|
||||
count = 0
|
||||
max_clicks = 0
|
||||
|
||||
|
||||
def download_image(url):
|
||||
try:
|
||||
# 获取图片的内容
|
||||
response = requests.get(url, stream=True)
|
||||
response.raise_for_status() # 检查请求是否成功
|
||||
|
||||
# 从URL中提取图片文件名
|
||||
filename = url.split("/")[-1]
|
||||
|
||||
# 确定保存路径
|
||||
filepath = os.path.join(os.getcwd(), filename)
|
||||
|
||||
# 检查文件是否已经存在,若存在则删除旧文件
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
print(f"文件 '{filename}' 已存在,旧文件已删除。")
|
||||
|
||||
# 以二进制写入方式保存图片
|
||||
with open(filepath, 'wb') as file:
|
||||
for chunk in response.iter_content(1024): # 分块下载
|
||||
file.write(chunk)
|
||||
|
||||
return filepath # 返回保存的文件路径
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
return None
|
||||
|
||||
# 鼠标点击事件的回调函数
|
||||
def mouse_callback(event, x, y, flags, param):
|
||||
global count, max_clicks, clicks
|
||||
|
||||
# 如果检测到左键点击事件
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
if count < max_clicks:
|
||||
count += 1
|
||||
clicks.append((x, y))
|
||||
# 在点击的位置绘制点击次数
|
||||
cv2.putText(img, str(count), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
|
||||
cv2.imshow('Image', img)
|
||||
|
||||
# 当达到指定的点击次数时,关闭窗口
|
||||
if count >= max_clicks:
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
# 主函数,接受图片路径和点击次数
|
||||
def captcha_main(image_path, num_clicks):
|
||||
global img, max_clicks, clicks, count
|
||||
clicks = []
|
||||
count = 0
|
||||
max_clicks = num_clicks
|
||||
|
||||
# 读取图像
|
||||
img = cv2.imread(image_path)
|
||||
if img is None:
|
||||
print("Error: Could not load image.")
|
||||
return
|
||||
|
||||
# 创建一个窗口并显示图片
|
||||
cv2.namedWindow('Image')
|
||||
cv2.imshow('Image', img)
|
||||
|
||||
# 设置鼠标点击回调函数
|
||||
cv2.setMouseCallback('Image', mouse_callback)
|
||||
|
||||
# 等待用户关闭窗口
|
||||
cv2.waitKey(0)
|
||||
|
||||
# 返回点击的坐标
|
||||
pos_list = [{'x': x, 'y': y} for x, y in clicks]
|
||||
return json.dumps(pos_list)
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# image_path = "G:\\Users\\15819\\Desktop\\14_3.png" # 替换为你的图片路径
|
||||
# num_clicks = 3 # 替换为你需要的点击次数
|
||||
# click_coordinates = captcha_main(image_path, num_clicks)
|
||||
# print("点击的坐标: ", click_coordinates)
|
@ -0,0 +1,208 @@
|
||||
import datetime
|
||||
import json
|
||||
import random
|
||||
import requests
|
||||
import time
|
||||
import captcha_img
|
||||
|
||||
|
||||
def get_timestamp():
|
||||
return str(round(datetime.datetime.now().timestamp(), 3))
|
||||
|
||||
|
||||
def parseMethodToken(test: str):
|
||||
return test[test.find('methodToken=') + 12:test.find('&csComm')]
|
||||
|
||||
#https://mcwk.mycourse.cn/course/A22002/A22002.html?userCourseId57051d99-434f-4814-81e7-84deee2128bb\u0026tenantCode\u003d4137011066\u0026type\u003d1\u0026csComm\u003dtrue\u0026csCapt\u003dtrue
|
||||
#https://mcwk.mycourse.cn/course/A22002/A22002.html?userCourseId=57051d99-434f-4814-81e7-84deee2128bb&tenantCode=4137011066&type=1&csComm=true&csCapt=true
|
||||
#https://mcwk.mycourse.cn/course/A22002/A22002.html?userCourseId=57051d99-434f-4814-81e7-84deee2128bb&tenantCode=4137011066&type=1&csComm=true&csCapt=true&userProjectId=5fc7738e-f98c-4890-8ce8-d3b06d2649da&userId=eec6b514-93d6-4516-9e6a-e45cc3ce980c&courseId=dd85f614-d32f-11eb-9a88-d4ae52bad611&projectType=special&projectId=undefined&protocol=true&link=34415&weiban=weiban&userName=f61f3acc85f34ae5802675ac194a5c38
|
||||
#https://weiban.mycourse.cn/#/course/detail?courseId=dd85f614-d32f-11eb-9a88-d4ae52bad611&userProjectId=5fc7738e-f98c-4890-8ce8-d3b06d2649da&courseName=%E5%8F%8D%E6%81%90%E4%B9%8B%E3%80%8A%E5%8F%8D%E6%81%90%E6%B3%95%E3%80%8B%E7%AF%87&userCourseId=57051d99-434f-4814-81e7-84deee2128bb&link=34415&projectType=special
|
||||
|
||||
def GetTimeStampMS():
|
||||
return str(int(round(time.time() * 1000)))
|
||||
|
||||
|
||||
class WeibanAPI:
|
||||
tenantCode = ''
|
||||
x_token = ' '
|
||||
userId = ' '
|
||||
userProjectId = ' '
|
||||
headers = None
|
||||
captcha_headers = None
|
||||
|
||||
def __init__(self, token, user_id, user_project_id, tenant_code):
|
||||
self.x_token = token
|
||||
self.tenantCode = tenant_code
|
||||
self.userId = user_id
|
||||
self.userProjectId = user_project_id
|
||||
self.headers = {
|
||||
'X-Token': self.x_token,
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
'Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.203'
|
||||
}
|
||||
self.captcha_headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
'Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.203',
|
||||
'Referer': 'https://mcwk.mycourse.cn/'
|
||||
}
|
||||
res = self._showProgress()
|
||||
if len(res) == 0:
|
||||
raise Exception('failed to get course info')
|
||||
|
||||
def showProgress(self):
|
||||
res = self._showProgress()
|
||||
j = json.loads(res)
|
||||
return j["data"]["requiredNum"], j["data"]["requiredFinishedNum"]
|
||||
|
||||
def _showProgress(self):
|
||||
url = 'https://weiban.mycourse.cn/pharos/project/showProgress.do?timestamp=' + get_timestamp()
|
||||
data = {
|
||||
'tenantCode': self.tenantCode,
|
||||
'userId': self.userId,
|
||||
'userProjectId': self.userProjectId
|
||||
}
|
||||
return self.process_url(url, data)
|
||||
|
||||
def _process_url(self, url, param, method):
|
||||
if method == 'POST':
|
||||
re = requests.post(url=url, data=param, headers=self.headers)
|
||||
elif method == 'GET':
|
||||
url += '?'
|
||||
for key, item in param.items():
|
||||
url = url + ('' if url.endswith('?') else '&') + str(key) + '=' + str(item)
|
||||
re = requests.get(url=url, headers=self.headers)
|
||||
else:
|
||||
raise ValueError('WRONG METHOD')
|
||||
return re.text
|
||||
|
||||
def process_url(self, url, param, method='POST'):
|
||||
return self._process_url(url, param, method)
|
||||
|
||||
def _listCategory(self):
|
||||
url = 'https://weiban.mycourse.cn/pharos/usercourse/listCategory.do?timestamp=' + get_timestamp()
|
||||
data = {
|
||||
'tenantCode': self.tenantCode,
|
||||
'userId': self.userId,
|
||||
'userProjectId': self.userProjectId,
|
||||
'chooseType': 3
|
||||
}
|
||||
return self.process_url(url, data)
|
||||
|
||||
def listCategory(self):
|
||||
ret = self._listCategory()
|
||||
try:
|
||||
j = json.loads(ret)
|
||||
return j["data"]
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print(e.msg)
|
||||
|
||||
def _listCourse(self, category_code):
|
||||
url = 'https://weiban.mycourse.cn/pharos/usercourse/listCourse.do?timestamp=' + get_timestamp()
|
||||
data = {
|
||||
'tenantCode': self.tenantCode,
|
||||
'userId': self.userId,
|
||||
'userProjectId': self.userProjectId,
|
||||
'chooseType': 3,
|
||||
'categoryCode': category_code
|
||||
}
|
||||
return self.process_url(url, data)
|
||||
|
||||
def listCourse(self, category_code):
|
||||
ret = self._listCourse(category_code)
|
||||
try:
|
||||
j = json.loads(ret)
|
||||
return j["data"]
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print(e.msg)
|
||||
|
||||
def _getCourseUrl(self, resource_id):
|
||||
url = 'https://weiban.mycourse.cn/pharos/usercourse/getCourseUrl.do?timestamp=' + get_timestamp()
|
||||
data = {
|
||||
'tenantCode': self.tenantCode,
|
||||
'userId': self.userId,
|
||||
'userProjectId': self.userProjectId,
|
||||
'courseId': resource_id
|
||||
}
|
||||
return self.process_url(url, data)
|
||||
|
||||
def getCourseUrl(self, resource_id):
|
||||
ret = self._getCourseUrl(resource_id)
|
||||
try:
|
||||
j = json.loads(ret)
|
||||
return j["data"]
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print(e.msg)
|
||||
|
||||
def methodToken(self, method_token, user_course_id):
|
||||
url = 'https://weiban.mycourse.cn/pharos/usercourse/v1/{}.do'.format(method_token)
|
||||
t = get_timestamp().replace('.', '')
|
||||
param = {
|
||||
'callback': 'jQuery341' + str(random.random()).replace('.', '') + '_' + t,
|
||||
'userCourseId': user_course_id,
|
||||
'tenantCode': self.tenantCode,
|
||||
'_': int(t) + 1
|
||||
}
|
||||
return self.process_url(url, param, 'GET')
|
||||
|
||||
def CheckCaptcha(self, url, answer):
|
||||
payload = {"coordinateXYs": answer}
|
||||
return requests.post(url, data=payload, headers=self.captcha_headers).text
|
||||
|
||||
def DoCaptcha(self, user_course_id):
|
||||
get_url = 'https://weiban.mycourse.cn/pharos/usercourse/getCaptcha.do'
|
||||
check_url = 'https://weiban.mycourse.cn/pharos/usercourse/checkCaptcha.do'
|
||||
#?userCourseId={}&userProjectId={}&userId={}&tenantCode={}
|
||||
param = {
|
||||
'userCourseId': user_course_id,
|
||||
'userProjectId': self.userProjectId,
|
||||
'userId': self.userId,
|
||||
'tenantCode': self.tenantCode
|
||||
}
|
||||
try:
|
||||
captcha_data = json.loads(self.process_url(get_url, param, 'GET'))
|
||||
captcha_num = captcha_data["captcha"]["num"]
|
||||
captcha_id = captcha_data["captcha"]["questionId"]
|
||||
captcha_image = captcha_img.download_image(captcha_data["captcha"]["imageUrl"])
|
||||
if(captcha_image is None):
|
||||
print('【错误】无法下载验证码')
|
||||
return None
|
||||
answer_pos = captcha_img.captcha_main(captcha_image, captcha_num)
|
||||
full_check_url = f'{check_url}?userCourseId={user_course_id}&userProjectId={self.userProjectId}&userId={self.userId}&tenantCode={self.tenantCode}&questionId={captcha_id}'
|
||||
captcha_result = json.loads(self.CheckCaptcha(full_check_url, answer_pos))
|
||||
if((captcha_result["code"] != "0") or (captcha_result["data"]["checkResult"] != 1)):
|
||||
print('【错误】验证码未通过')
|
||||
return None
|
||||
return captcha_result["data"]["methodToken"]
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print(f'JSON Error: {e.msg}')
|
||||
return None
|
||||
|
||||
def MakeCourseFinish(self, captcha_token, user_course_id):
|
||||
url = 'https://weiban.mycourse.cn/pharos/usercourse/v2/{}.do'.format(captcha_token)
|
||||
t = GetTimeStampMS()
|
||||
param = {
|
||||
'callback': 'jQuery341' + str(random.random()).replace('.', '') + '_' + t,
|
||||
'userCourseId': user_course_id,
|
||||
'tenantCode': self.tenantCode,
|
||||
'_': int(t) + 1
|
||||
}
|
||||
return self.process_url(url, param, 'GET')
|
||||
|
||||
def study(self, resource_id):
|
||||
res = self._study(resource_id)
|
||||
try:
|
||||
j = json.loads(res)
|
||||
return j["code"]
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
print(e.msg)
|
||||
|
||||
def _study(self, resource_id):
|
||||
url = 'https://weiban.mycourse.cn/pharos/usercourse/study.do?timestamp=' + get_timestamp()
|
||||
data = {
|
||||
'tenantCode': self.tenantCode,
|
||||
'userId': self.userId,
|
||||
'userProjectId': self.userProjectId,
|
||||
'courseId': resource_id
|
||||
}
|
||||
return self.process_url(url, data)
|
Loading…
Reference in New Issue