diff --git a/README.md b/README.md index a5a9df07..7d8655b3 100644 --- a/README.md +++ b/README.md @@ -129,11 +129,9 @@ TechXueXi为python学 xi 交流的开源非营利项目,仅作为程序员之 # 📑常见问题 -win平台可能提示`无法定位程序输入点ucrtbase.terminate于动态链接库api-ms-win-crt-runtime-|1-1-0.dll`等缺失dll文件的问题而无法使用,尝试安装`Visual C++ Redistributable for Visual Studio 2015` +win平台可能提示`无法定位程序输入点ucrtbase.terminate于动态链接库api-ms-win-crt-runtime-|1-1-0.dll`等缺失dll文件的问题而无法使用,尝试安装[`Visual C++ Redistributable for Visual Studio 2015`](https://www.microsoft.com/zh-CN/download/details.aspx?id=48145) -链接已失效,请自己百度下载 - -**下载安装:** +**下载链接:** [![](https://img.shields.io/badge/download-vc_redist.x64-blue.svg?style=for-the-badge&logo=visualstudiocode)](https://raw.fastgit.org/TechXueXi/VC-REDIST/master/vc_redist.x64.exe) diff --git a/SourcePackages/pandalearning.py b/SourcePackages/pandalearning.py index 8ef5f5e8..510ccf57 100644 --- a/SourcePackages/pandalearning.py +++ b/SourcePackages/pandalearning.py @@ -72,6 +72,7 @@ def start_learn(uid, name): driver_login = Mydriver() cookies = driver_login.login() driver_login.quit() + # cookies = login() if not cookies: print("登录超时") return @@ -98,8 +99,8 @@ def start_learn(uid, name): article_thread.join() video_thread.join() if TechXueXi_mode in ["2", "3"]: - print('开始每日答题……') - daily(cookies, scores) +# print('开始每日答题……') +# daily(cookies, scores) print('开始每周答题……') weekly(cookies, scores) if nohead != True or gl.zhuanxiang == True: @@ -133,7 +134,7 @@ def start(nick_name=None): user_list.append(["", "新用户"]) for i in range(len(user_list)): try: - if nick_name == None or nick_name == user_list[i][1] or nick_name == user_list[i][0]: + if nick_name is None or nick_name == user_list[i][1] or nick_name == user_list[i][0]: _learn = threads.MyThread( user_list[i][0]+"开始学xi", start_learn, user_list[i][0], user_list[i][1], lock=Single) _learn.start() @@ -177,6 +178,7 @@ def add_user(chat_id=None): driver_login = Mydriver() cookies = driver_login.login(chat_id) driver_login.quit() + # cookies = login(chat_id=chat_id) if not cookies: gl.pushprint("登录超时。", chat_id=chat_id) return diff --git a/SourcePackages/pdlearn/answer_question.py b/SourcePackages/pdlearn/answer_question.py index 690f9591..a3a0bc81 100644 --- a/SourcePackages/pdlearn/answer_question.py +++ b/SourcePackages/pdlearn/answer_question.py @@ -24,12 +24,15 @@ def generate_tiku_data(quiz_type=None, tip=None, option=None, answer=None, quest def find_available_quiz(quiz_type, driver_ans, uid): - pages = driver_ans.driver.find_elements_by_css_selector( + all_pages = driver_ans.driver.find_elements_by_css_selector( ".ant-pagination-item") - for p in range(0, len(pages), 1): # (从最后一页开始往前找做题)从前往后找题,专项答题等没有那么离谱 + pages=int(all_pages[-1].get_attribute('title')) + # page_next= driver_ans.driver.find_elements_by_css_selector('ant-pagination-item-link') + print('总页数'+str(pages)) + for p in range(0, pages, 1): # (从最后一页开始往前找做题)从前往后找题,专项答题等没有那么离谱 time.sleep(0.5) print('进入答题第' + str(p+1) + '页') - pages[p].click() + # page_next[0].click() time.sleep(0.5) dati = [] if quiz_type == "weekly": # 寻找可以做的题 @@ -47,6 +50,39 @@ def find_available_quiz(quiz_type, driver_ans, uid): to_click = j # auto.prompt("wait for Enter press...") return to_click + page_next= driver_ans.driver.find_elements_by_css_selector('.ant-pagination-next') + page_next[0].click() +# # 这里有些问题,专项学习一开始会出现 1 2 3 4 5 10,一共6个button +# # 第一次find_elements_by_css_selector以后只会学到前5页和最后一页,这6页学完以后就会找不到 +# # 这里改动先找最后一个button的页码,然后每次点击,更新pages,模拟手动点击的过程 +# prestr = ".ant-pagination-item" +# pages = driver_ans.driver.find_elements_by_css_selector(prestr) +# last = int(pages[len(pages)-1].text) +# for p in range(0, last, 1): # (从最后一页开始往前找做题)从前往后找题,专项答题等没有那么离谱 +# pages = driver_ans.driver.find_elements_by_css_selector(prestr) +# for index in range(0, len(pages), 1): +# if pages[index].text == str(p+1): +# time.sleep(0.5) +# print('进入答题第' + str(p+1) + '页') +# pages[index].click() +# time.sleep(0.5) +# break +# dati = [] +# if quiz_type == "weekly": # 寻找可以做的题 +# dati = driver_ans.driver.find_elements_by_css_selector( +# "#app .month .week button") +# elif quiz_type == "zhuanxiang": # 寻找可以做的题 +# # 可以使用 #app .items .item button:not(.ant-btn-background-ghost) 选择器,但会遗漏掉”继续答题“的部分 +# dati = driver_ans.driver.find_elements_by_css_selector( +# "#app .items .item button") +# for i in range(len(dati)-1, -1, -1): # 从最后一个遍历到第一个 +# j = dati[i] +# if ("重新" in j.text or "满分" in j.text): +# continue +# else: +# to_click = j +# # auto.prompt("wait for Enter press...") +# return to_click @exception_catcher() @@ -492,9 +528,10 @@ def answer_question(quiz_type, cookies, scores, score_all, quiz_xpath, category_ if scores[quiz_type] >= score_all: print("检测到"+quiz_zh_CN[quiz_type]+"答题分数已满,退出学 xi ") else: - print( - "!!!!!没拿到满分,请收集日志反馈错误题目!!!!!https://github.com/TechXueXi/techxuexi-tiku/issues/1") - auto.prompt("完成后(或懒得弄)请在此按回车...") + if quiz_type != "weekly": + print( + "!!!!!没拿到满分,请收集日志反馈错误题目!!!!!https://github.com/TechXueXi/techxuexi-tiku/issues/1") + auto.prompt("完成后(或懒得弄)请在此按回车...") # log_daily("!!!!!没拿到满分!!!!!") if driver_default == None: try: diff --git a/SourcePackages/pdlearn/const.py b/SourcePackages/pdlearn/const.py index df059f66..b308b495 100644 --- a/SourcePackages/pdlearn/const.py +++ b/SourcePackages/pdlearn/const.py @@ -6,4 +6,4 @@ class const: login_all = 1 # 每日登陆 daily_all = 5 # 每日答题 weekly_all = 5 # 每周答题 - zhuanxiang_all = 10 # 专项答题 \ No newline at end of file + zhuanxiang_all = 5 # 专项答题 diff --git a/SourcePackages/pdlearn/file.py b/SourcePackages/pdlearn/file.py index 271e177f..797dcd33 100644 --- a/SourcePackages/pdlearn/file.py +++ b/SourcePackages/pdlearn/file.py @@ -58,7 +58,7 @@ def save_json_data(filename, object_to_save, sort_keys=True): def get_conf_file(filename, template_conf_str): check_directory(filename) - if(os.path.exists(filename) and os.path.getsize(filename) != 0): + if os.path.exists(filename) and os.path.getsize(filename) != 0: try: conf_obj = ConfigFactory.parse_file(filename) except Exception as e: @@ -74,4 +74,4 @@ def get_conf_file(filename, template_conf_str): def save_text_file(filename, text): check_directory(filename) with open(filename, 'w', encoding='utf-8') as o: - o.write(text) + o.write(text) \ No newline at end of file diff --git a/SourcePackages/pdlearn/get_links.py b/SourcePackages/pdlearn/get_links.py index 4f1cf713..7446bde7 100644 --- a/SourcePackages/pdlearn/get_links.py +++ b/SourcePackages/pdlearn/get_links.py @@ -25,23 +25,21 @@ def get_article_links(): def get_video_links(): try: - video_json = requests.get("https://www.xuexi.cn/lgdata/4426aa87b0b64ac671c96379a3a8bd26/db086044562a57b441c24f2af1c8e101.json").content.decode("utf8") - video=json.loads(video_json)["DataSet"] + # 解决视频不能正常学习的问题 + video_json = requests.get( + "https://www.xuexi.cn/lgdata/4426aa87b0b64ac671c96379a3a8bd26/db086044562a57b441c24f2af1c8e101.json").content.decode( + "utf8") + video = json.loads(video_json)["DataSet"] json_urls = [] - link = [] for i in video: - json_urls.append("https://www.xuexi.cn/lgdata/"+i.split('!')[1]) - while len(link) < 20: - choose_json_url = random.choice(json_urls) - choose_json_str = requests.get(choose_json_url).content.decode("utf8") - pattern = r'https://www.xuexi.cn/[^,"]*' - choose_links = re.findall(pattern, choose_json_str, re.I) - if(len(choose_links) >= 5): - choose_sample = random.sample(choose_links, 5) - for c in choose_sample: - link.append(c) - random.shuffle(link) - return link + json_urls.append("https://www.xuexi.cn/lgdata/" + i.split('!')[1]) + + all_video_object = [] + for url in json_urls: + choose_json_str = requests.get(url).content.decode("utf8") + all_video_object.extend(json.loads(choose_json_str)) + new_list = sorted(all_video_object, key=lambda x: x.get("publishTime", "0"), reverse=False) + return [news["url"] for news in new_list] except: print("=" * 60) print("get_video_links获取失败") diff --git a/SourcePackages/pdlearn/historical/pandalearning.py b/SourcePackages/pdlearn/historical/pandalearning.py index 666f8e81..532e8215 100644 --- a/SourcePackages/pdlearn/historical/pandalearning.py +++ b/SourcePackages/pdlearn/historical/pandalearning.py @@ -1,5 +1,7 @@ from pdlearn import dingding from pdlearn import user +from pdlearn.mydriver import Mydriver + def user_flag(dd_status, uname): if False and dd_status: @@ -13,6 +15,7 @@ def user_flag(dd_status, uname): driver_login = Mydriver(nohead=False) cookies = driver_login.login() driver_login.quit() + # cookies = login() else: cookies = dingding.dd_login_status(uname) a_log = user.get_a_log(uname) diff --git a/SourcePackages/pdlearn/mydriver.py b/SourcePackages/pdlearn/mydriver.py index 78e05d5c..8e9c8f29 100644 --- a/SourcePackages/pdlearn/mydriver.py +++ b/SourcePackages/pdlearn/mydriver.py @@ -5,10 +5,12 @@ import re import string import time +import uuid from typing import Any, List from urllib.parse import quote, quote_plus import lxml +import qrcode import requests import selenium from bs4 import BeautifulSoup @@ -22,27 +24,112 @@ from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait + +from pdlearn.globalvar import web from webServerConf import WebMessage, WebQrUrl, web_db -from pdlearn import auto -from pdlearn import globalvar as gl +from pdlearn import globalvar as gl, auto from pdlearn import user, user_agent from pdlearn.web import WebHandler from pdlearn.config import cfg_get from pdlearn.dingding import DingDingHandler -# from pdlearn.qywx import WeChat # 使用微信发送二维码图片到手机 +# from pdlearn.qywx import WeChat # 使用微信发送二维码图片到手机 + def decode_img(data): if None == data: raise Exception('未获取到二维码,请检查网络并重试') - img_b64decode = base64.b64decode(data[data.index(';base64,')+8:]) + img_b64decode = base64.b64decode(data[data.index(';base64,') + 8:]) decoded = pyzbar.decode(Image.open(io.BytesIO(img_b64decode))) return decoded[0].data.decode("utf-8") +def login(chat_id=None): + client = requests.session() + # 1. 获取sign + sign: str = client.get(url="https://pc-api.xuexi.cn/open/api/sns/sign").json().get("data").get("sign") + # 2. 获取qr + qr_data: str = client.get("https://login.xuexi.cn/user/qrcode/generate").json().get("result") + # 3. 生成登录链接 + code_url = f"https://login.xuexi.cn/login/qrcommit?showmenu=false&code={qr_data}&appId=dingoankubyrfkttorhpou" + # 生成二维码 + qr = qrcode.QRCode(version=None, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=10, border=2) + qr.add_data(code_url) + qr.make(fit=True) + img = qr.make_image() + img.show() + img.save("qrcode.png") + print("二维码已保存到qrcode.png") + # 二维码转base64 + output_buffer = io.BytesIO() + img.save(output_buffer, format='JPEG') + byte_data = output_buffer.getvalue() + qrbase64 = "data:image/png;base64," + base64.b64encode(byte_data).decode("utf-8") + + # 推送消息 + if gl.nohead or cfg_get("addition.SendLoginQRcode", 0) == 1: + print("二维码将发往机器人...\n" + "=" * 60) + # 发送二维码 + gl.send_qrbase64(qrbase64) + # 发送链接 + if gl.scheme: + qrurl = gl.scheme + quote_plus(code_url) + else: + qrurl = decode_img(qrbase64) + gl.pushprint(qrurl, chat_id) + + web_qr_url = "" + web_msg = "" + try: + web_qr_url = web_db.session.query( + WebQrUrl).filter_by(url=qrbase64).first() + web_msg = web_db.session.query( + WebMessage).filter_by(text=code_url).first() + except Exception as e: + print(str(e)) + print("web数据库添加失败") + web_db.session.rollback() + + secret = "" + print(f"sign: {sign}, data: {qr_data}") + for i in range(60): + resp = client.post(url="https://login.xuexi.cn/login/login_with_qr", + data={"qrCode": qr_data, "goto": "https://oa.xuexi.cn", "pdmToken": ""}, + ).json() + if resp.get("success"): + secret = resp.get("data") + break + else: + print("等待扫码中---") + print(resp) + time.sleep(5) + if secret == "": + if gl.islooplogin: + print("循环模式开启,即将重新获取二维码") + time.sleep(3) + return login(chat_id) + return None + client.get("https://pc-api.xuexi.cn/login/secure_check", + params={"code": secret.split("=")[1], "state": sign + str(uuid.uuid4())}) + + print("token ==> " + client.cookies.get("token")) + cookies = [ + {'domain': '.xuexi.cn', + "expiry": int(time.time())+12*3600, + 'httpOnly': False, + 'name': 'token', 'path': '/', + 'secure': False, + 'value': client.cookies.get("token")}] + user.save_cookies(cookies) + web_qr_url and web_db.session.delete(web_qr_url) + web_msg and web_db.session.delete(web_msg) + web_db.session.commit() + return cookies + + class title_of_login: def __call__(self, driver): """ 用来结合webDriverWait判断出现的title """ @@ -50,7 +137,7 @@ def __call__(self, driver): is_title1 = bool(EC.title_is(u'我的学习')(driver)) is_title2 = bool(EC.title_is(u'系统维护中')(driver)) except Exception as e: - print("chrome 开启失败。"+str(e)) + print("chrome 开启失败。" + str(e)) exit() if is_title1 or is_title2: return True @@ -99,6 +186,10 @@ def __init__(self, noimg=True, nohead=True): self.options.add_argument("--disable-blink-features") self.options.add_argument( "--disable-blink-features=AutomationControlled") + # 设置为不走代理 + self.options.add_argument('--proxy-server="direct://"') + self.options.add_argument('--proxy-bypass-list=*') + self.webdriver = webdriver # ==================== 寻找 chrome ==================== @@ -110,35 +201,35 @@ def __init__(self, noimg=True, nohead=True): mydriver_log = '可找到 "/opt/google/chrome/chrome"' # ==================== 寻找 chromedriver ==================== chromedriver_paths = [ - "./chrome/chromedriver.exe", # win - "./chromedriver", # linux - "/usr/bin/chromedriver", # linux用户安装 + "./chrome/chromedriver.exe", # win + "./chromedriver", # linux + "/usr/bin/chromedriver", # linux用户安装 # raspberry linux (需要包安装chromedriver) "/usr/lib64/chromium-browser/chromedriver", # raspberry linux (需要包安装chromedriver) "/usr/lib/chromium-browser/chromedriver", - "/usr/local/bin/chromedriver", # linux 包安装chromedriver + "/usr/local/bin/chromedriver", # linux 包安装chromedriver ] have_find = False for one_path in chromedriver_paths: if os.path.exists(one_path): self.driver = self.webdriver.Chrome( executable_path=one_path, chrome_options=self.options) - mydriver_log = mydriver_log+'\r\n可找到 "' + one_path + '"' + mydriver_log = mydriver_log + '\r\n可找到 "' + one_path + '"' have_find = True break if not have_find: self.driver = self.webdriver.Chrome( chrome_options=self.options) - mydriver_log = mydriver_log+'\r\n未找到chromedriver,使用默认方法。' + mydriver_log = mydriver_log + '\r\n未找到chromedriver,使用默认方法。' except: print("=" * 60) print(" Chrome 浏览器初始化失败。信息:") print(mydriver_log) print('您可以检查下:') - print("1. 是否存在./chrome/chromedriver.exe 或 PATH 中是否存在 chromedriver.exe") + print("1. 是否存在./chrome/chromedriver 或 PATH 中是否存在 chromedriver") print( - "2. 浏览器地址栏输入 chrome://version 看到的chrome版本 和 运行 chromedriver.exe 显示的版本整数部分是否相同") + "2. 浏览器地址栏输入 chrome://version 看到的chrome版本 和 运行 chromedriver 显示的版本整数部分是否相同") print("针对上述问题,请在 https://registry.npmmirror.com/binary.html?path=chromedriver/ 下载对应版本程序并放在合适的位置") print("3. 如不是以上问题,请提issue,附上报错信息和您的环境信息") print("=" * 60) @@ -170,7 +261,7 @@ def get_cookie_from_network(self, chat_id=None): print("当前网络缓慢...") else: self.driver.execute_script('arguments[0].remove()', remover) - #修改了适配新版本的二维码的滚动位置 + # 修改了适配新版本的二维码的滚动位置 self.driver.execute_script( 'window.scrollTo(document.body.scrollWidth/2 - 200 , 400)') qrurl = '' @@ -223,11 +314,11 @@ def get_cookie_from_network(self, chat_id=None): web_msg and web_db.session.delete(web_msg) web_db.session.commit() return cookies - + except Exception as e: print("扫描二维码超时... 错误信息:" + str(e)) self.web_log("扫描二维码超时... 错误信息:" + str(e)) - if(gl.islooplogin == True): + if (gl.islooplogin == True): print("循环模式开启,即将重新获取二维码") self.web_log("循环模式开启,即将重新获取二维码") time.sleep(3) @@ -254,7 +345,7 @@ def sendmsg(self, chat_id=None): # 发送链接 qrurl = '' if gl.scheme: - qrurl = gl.scheme+quote_plus(decode_img(qcbase64)) + qrurl = gl.scheme + quote_plus(decode_img(qcbase64)) else: qrurl = decode_img(qcbase64) gl.pushprint(qrurl, chat_id) @@ -408,7 +499,7 @@ def _view_tips(self): # by Sean display_tip = 0 # 页面上没有加载提示的内容 display_tip = self.driver.find_element_by_css_selector( ".ant-popover-hidden") # 关闭tip则为hidden - if(display_tip == 0): # 没有关闭tip + if (display_tip == 0): # 没有关闭tip tips_close = self.driver.find_element_by_xpath( '//*[@id="app"]/div/div[2]/div/div[4]/div[1]/div[1]') tips_close.click() @@ -475,6 +566,7 @@ def swiper_valid(self): builder.release().perform() time.sleep(5) self.swiper_valid() + # 鼠标移动 def move_mouse(self, distance): @@ -612,4 +704,4 @@ def _search(self, content, options, exclude=''): _, i = random.choice(counts) print(f'搜索结果全0,随机一个 {i}') print(f'根据搜索结果: {i} 很可能是正确答案') - return i + return i \ No newline at end of file diff --git a/SourcePackages/pdlearn/score.py b/SourcePackages/pdlearn/score.py index f4c1fc59..245b11bb 100644 --- a/SourcePackages/pdlearn/score.py +++ b/SourcePackages/pdlearn/score.py @@ -7,6 +7,8 @@ from pdlearn import file from pdlearn.const import const import threading +import time +import datetime # 总积分 @@ -63,7 +65,8 @@ def get_score(cookies): jar = RequestsCookieJar() for cookie in cookies: jar.set(cookie['name'], cookie['value']) - total_json = requests.get("https://pc-api.xuexi.cn/open/api/score/get", cookies=jar, + t = time.time() + total_json = requests.get("https://pc-proxy-api.xuexi.cn/delegate/score/get?_t=%d"%(int(round(t * 1000))), cookies=jar, headers={'Cache-Control': 'no-cache'}).content.decode("utf8") if not json.loads(total_json)["data"]: globalvar.pushprint("cookie过期,请重新登录", chat_id) @@ -83,7 +86,7 @@ def get_score(cookies): # headers={'Cache-Control': 'no-cache'}).content.decode("utf8") today = 0 # today = int(json.loads(today_json)["data"]["score"]) - score_json = requests.get("https://pc-proxy-api.xuexi.cn/api/score/days/listScoreProgress?sence=score&deviceType=2", cookies=jar, + score_json = requests.get("https://pc-proxy-api.xuexi.cn/delegate/score/days/listScoreProgress?sence=score&deviceType=2", cookies=jar, headers={'Cache-Control': 'no-cache'}).content.decode("utf8") dayScoreDtos = json.loads(score_json)["data"] today = dayScoreDtos["totalScore"] diff --git a/SourcePackages/pdlearn/version_info.json b/SourcePackages/pdlearn/version_info.json index 8be8d496..fe5ab3ec 100644 --- a/SourcePackages/pdlearn/version_info.json +++ b/SourcePackages/pdlearn/version_info.json @@ -1,9 +1,15 @@ { - "techxuexi_version": "v20220411", + "techxuexi_version": "v20230521", "notice": "欢迎使用 TechXueXi", "least_version": "v20211020", "old_version_warning": "您的版本太旧,请立即更新", "techxuexi_update_log": [{ + "version": "v20230521", + "info": "修复若干问题" + },{ + "version": "v20220711", + "info": "修复qrcode的问题" + },{ "version": "v20220411", "info": "修复Linux or macOS上Pillow的问题" },{ diff --git a/requirements.txt b/requirements.txt index f2115b1b..9e037529 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,6 +11,7 @@ gunicorn==20.1.0 PySocks==1.7.1 urllib3==1.26.7 itsdangerous==2.0.1 -Flask==1.1.2 +Flask==2.1.3 Flask_SQLAlchemy==2.5.1 Flask_Cors==3.0.9 +qrcode==7.3.1 \ No newline at end of file