├── requirements.txt
├── inside
├── __init__.py
├── Baidu_AI
│ ├── __init__.py
│ ├── Baidu_AI_Manage.py
│ ├── Baidu_AI_Verify.py
│ └── Baidu_AI_Tools.py
├── Config
│ ├── __init__.py
│ ├── Path.py
│ ├── User_Agent.py
│ ├── System.py
│ ├── SystemArgs.py
│ └── Api.py
├── DB
│ ├── __init__.py
│ ├── Table_Class
│ │ ├── __init__.py
│ │ ├── Project.py
│ │ ├── Baidu_AI.py
│ │ ├── User.py
│ │ ├── Task.py
│ │ ├── Video.py
│ │ └── Article.py
│ ├── DB_Project.py
│ ├── DB_Manage.py
│ ├── DB_Baidu_AI.py
│ ├── DB_Task.py
│ ├── DB_User.py
│ ├── DB_Video.py
│ ├── DB_Config.py
│ ├── DB_Create.py
│ ├── DB_Article.py
│ └── DB_Check.py
├── Driver
│ ├── __init__.py
│ ├── Driver_Check.py
│ ├── Driver_Download.py
│ ├── Driver_Manage.py
│ ├── Driver_Init.py
│ └── Driver_Analysis.py
├── Info
│ ├── __init__.py
│ ├── Task_Info.py
│ ├── Info_Manage.py
│ ├── Get_Info.py
│ └── User_Info.py
├── Login
│ ├── __init__.py
│ ├── Check_Token.py
│ ├── QR_Vessel.py
│ └── Login.py
├── Task
│ ├── __init__.py
│ ├── Mitmdump
│ │ ├── __init__.py
│ │ ├── Intercept
│ │ │ ├── __init__.py
│ │ │ ├── ABC_Answer.py
│ │ │ ├── Script.py
│ │ │ ├── Daily.py
│ │ │ ├── Weekly.py
│ │ │ └── Project.py
│ │ └── Mitmdump.py
│ ├── Task_Article_Video.py
│ ├── Task_Init.py
│ ├── Task_Answer.py
│ └── Task_Manage.py
├── Tools
│ ├── __init__.py
│ ├── Quit.py
│ ├── Output.py
│ ├── Url_Test.py
│ ├── Random.py
│ ├── Requests.py
│ └── Network.py
├── Options
│ ├── __init__.py
│ ├── Options_Manage.py
│ └── Options.py
└── Template
│ ├── __init__.py
│ ├── Task_Exception.py
│ ├── Meta_Singleton.py
│ ├── ABC_Driver_Analysis.py
│ └── ABC_System_Args.py
├── LICENSE
├── main.py
└── README.md
/requirements.txt:
--------------------------------------------------------------------------------
1 | mitmproxy~=6.0.2
2 | tqdm~=4.56.0
3 | selenium~=3.141.0
4 | requests~=2.25.1
5 | beautifulsoup4~=4.9.3
6 | mitmproxy~=6.0.2
7 | lxml~=4.6.2
8 | psutil~=5.8.0
9 | opencv-python~=4.5.1.48
10 | Pillow~=8.1.0
--------------------------------------------------------------------------------
/inside/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/14
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 内部
9 |
--------------------------------------------------------------------------------
/inside/Baidu_AI/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function :
9 |
--------------------------------------------------------------------------------
/inside/Config/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/14
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 配置
9 |
--------------------------------------------------------------------------------
/inside/DB/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 数据库
9 |
--------------------------------------------------------------------------------
/inside/Driver/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/16
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 驱动
9 |
--------------------------------------------------------------------------------
/inside/Info/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/22
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 信息
9 |
--------------------------------------------------------------------------------
/inside/Login/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 登录
9 |
--------------------------------------------------------------------------------
/inside/Task/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/24
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 任务
9 |
--------------------------------------------------------------------------------
/inside/Tools/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 工具
9 |
--------------------------------------------------------------------------------
/inside/Options/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/23
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 选项
9 |
--------------------------------------------------------------------------------
/inside/Template/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/14
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 模板
9 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 数据对象
9 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/26
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function : 拦截注入
9 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Intercept/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : __init__.py.py
8 | # @Function :
9 |
--------------------------------------------------------------------------------
/inside/Template/Task_Exception.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/4
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Exception.py
8 | # @Function : 任务异常
9 |
10 | class TASK_EXCEPTION(Exception):
11 | pass
12 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/Project.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Project.py
8 | # @Function : 专项答题类
9 | __all__ = ['PROJECT']
10 |
11 |
12 | class PROJECT(object):
13 |
14 | def __init__(self, pid: int):
15 | """
16 | PROJECT(pid: int)
17 | 初始化
18 |
19 | @param pid: 专项答题ID
20 | """
21 | self.__pid = pid
22 |
23 | @property
24 | def Id(self) -> int:
25 | """
26 | Id -> int
27 |
28 | @return: 专项答题ID
29 | """
30 | return self.__pid
31 |
--------------------------------------------------------------------------------
/inside/Tools/Quit.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/25
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Quit.py
8 | # @Function : 退出
9 | from inside.DB.DB_Manage import DB_MANAGE
10 | from inside.Driver.Driver_Manage import DRIVER_MANAGE
11 | from inside.Task.Mitmdump.Mitmdump import MITMDUMP
12 | from inside.Tools.Network import NETWORK
13 |
14 | __all__ = ['Quit']
15 |
16 |
17 | def Quit() -> None:
18 | """
19 | Quit() -> None
20 | 有序退出程序
21 |
22 | :return: None
23 | """
24 | NETWORK().Quit()
25 | temp = DRIVER_MANAGE().Task_Quit
26 | DB_MANAGE().Quit()
27 | MITMDUMP().Close()
28 | del temp
29 |
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 lisztomania
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/20
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : main.py
8 | # @Function : 入口
9 | import os
10 |
11 | from inside.Config.System import SYSTEM
12 | from inside.Driver.Driver_Manage import DRIVER_MANAGE
13 | from inside.Login.Login import LOGIN
14 | from inside.Options.Options_Manage import OPTIONS_MANAGE
15 | from inside.Task.Mitmdump.Mitmdump import MITMDUMP
16 | from inside.Task.Task_Manage import TASK_MANAGE
17 | from inside.Tools.Network import NETWORK
18 | from inside.Tools.Output import OUTPUT
19 |
20 | if __name__ == '__main__':
21 | SYSTEM().Check_Chrome()
22 | os.system(command=SYSTEM().Clear)
23 | OPTIONS_MANAGE.Init_Options()
24 | MITMDUMP().Open()
25 | driver = DRIVER_MANAGE()
26 | network = NETWORK()
27 | network.Init(driver=driver.Task)
28 | if LOGIN(task_driver=driver.Task).Login():
29 | while True:
30 | OUTPUT.Info()
31 | OPTIONS_MANAGE.Task_Options()
32 | task = TASK_MANAGE(driver=driver.Task)
33 | task.Task()
34 |
35 |
--------------------------------------------------------------------------------
/inside/Login/Check_Token.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/20
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Check_Token.py
8 | # @Function : 检测令牌
9 | from inside.Config.Api import API
10 | from inside.Template.Meta_Singleton import SINGLETON
11 |
12 | from inside.Tools.Random import RANDOM
13 | from inside.Tools.Requests import REQUESTS
14 |
15 | __all__ = ['CHECK_TOKEN']
16 |
17 |
18 | class CHECK_TOKEN(metaclass=SINGLETON):
19 | """检查令牌类"""
20 |
21 | @classmethod
22 | def Check_Token(cls, token: str) -> bool:
23 | """
24 | Check_Token(token: str) -> bool
25 | 检查令牌是否有效
26 |
27 | :param token: 令牌
28 | :return: bool
29 | """
30 | cookie = {'token': token}
31 | link = API().Token_Check.geturl().format(
32 | timestamp=RANDOM().Float_Precision(a=0, b=1, precision=16)
33 | )
34 | html = REQUESTS.Get(
35 | url=link,
36 | cookies=cookie
37 | )
38 | if html.cookies.get(name='token'):
39 | return True
40 | return False
41 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Mitmdump.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/26
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Mitmdump.py
8 | # @Function : 拦截注入器
9 | import subprocess
10 |
11 | from inside.Config.Path import PATH
12 | from inside.Template.Meta_Singleton import SINGLETON
13 | __all__ = ['MITMDUMP']
14 |
15 |
16 | class MITMDUMP(metaclass=SINGLETON):
17 | """拦截注入器"""
18 | __mitmdump: subprocess.Popen
19 |
20 | def __init__(self):
21 | """
22 | MITMDUMP()
23 | 初始化
24 |
25 | """
26 | self.__self = '_'+type(self).__name__
27 |
28 | def Open(self) -> None:
29 | """
30 | Open() -> None
31 | 打开
32 |
33 | :return: None
34 | """
35 | if not hasattr(self, self.__self+'__mitmdump') or self.__mitmdump.poll():
36 | self.__mitmdump = subprocess.Popen(
37 | ['mitmdump', '-q', '-p', '8080', '-s', PATH().Script]
38 | )
39 |
40 | def Close(self) -> None:
41 | """
42 | Close() -> None
43 | 关闭
44 |
45 | :return: None
46 | """
47 | if hasattr(self, self.__self+'__mitmdump'):
48 | self.__mitmdump.kill()
49 |
--------------------------------------------------------------------------------
/inside/Tools/Output.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Output.py
8 | # @Function : 信息输出
9 | import os
10 |
11 | from inside.Config.System import SYSTEM
12 | from inside.Info.Info_Manage import INFO_MANAGE
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['OUTPUT', 'Info']
16 |
17 |
18 | class OUTPUT(metaclass=SINGLETON):
19 | """
20 | 输出信息
21 | """
22 |
23 | @classmethod
24 | def Info(cls):
25 | os.system(command=SYSTEM().Clear)
26 | temp = INFO_MANAGE()
27 | level = temp.Level_Info
28 | print(f"项目地址:https://github.com/lisztomania-Zero/learning-power")
29 | print(f"用户ID:{temp.Id}\n"
30 | f"等级:{level[0]}\t段位:{level[1]}\t全国排名:{level[2]}\n"
31 | f"总积分:{temp.Aggregate_Score}\t今日积分:{temp.Daily_Score}\n"
32 | f"每日积分细则:")
33 | print()
34 | for index, value in enumerate(list(temp.Task_Bar.values())):
35 | if (index+1) % 5 == 0:
36 | print(f"{value.Name}:{value.Current_Score}分")
37 | continue
38 | print(f"{value.Name}:{value.Current_Score}分\t", end='')
39 | print('\n')
40 |
41 |
42 | _inst = OUTPUT()
43 | Info = _inst.Info
44 |
--------------------------------------------------------------------------------
/inside/Tools/Url_Test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/28
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Url_Test.py
8 | # @Function : 链接延迟测试
9 | import time
10 | import ssl
11 | from socket import timeout
12 | from urllib import request
13 | from urllib.error import URLError
14 |
15 | from inside.Template.Meta_Singleton import SINGLETON
16 |
17 | __all__ = ['URL_TEST', 'Url_Test']
18 |
19 |
20 | class URL_TEST(metaclass=SINGLETON):
21 | """url延迟测试类"""
22 | ssl._create_default_https_context = ssl._create_unverified_context
23 |
24 | @classmethod
25 | def Url_Test(cls, url: str) -> float:
26 | """
27 | Url_Test(url: str) -> float
28 | 测试url访问延迟
29 |
30 | :param url: url
31 | :return: float
32 | """
33 | try:
34 | temp = 0
35 | for _ in range(3):
36 | s = time.time()
37 | try:
38 | request.urlopen(url=url, timeout=3)
39 | except (timeout, URLError):
40 | pass
41 | temp += time.time() - s
42 | return temp / 3
43 | except (ValueError, AttributeError):
44 | raise Exception(f"{url}:不是正确的url链接")
45 |
46 |
47 | _inst = URL_TEST
48 | Url_Test = _inst.Url_Test
49 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/Baidu_AI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Baidu_AI.py
8 | # @Function : 百度AI类
9 | __all__ = ['BAIDU_AI']
10 |
11 |
12 | class BAIDU_AI(object):
13 | """百度AI类"""
14 |
15 | def __init__(self, ak: str, sk: str):
16 | """
17 | BAIDU_AI(ak: str, sk: str)
18 | 初始化
19 |
20 | @param ak: API Key
21 | @param sk: Secret Key
22 | """
23 | self.__ak = ak
24 | self.__sk = sk
25 |
26 | @property
27 | def Ak(self) -> str:
28 | """
29 | Ak -> str
30 |
31 | @return: API Key
32 | """
33 | return self.__ak
34 |
35 | @Ak.setter
36 | def Ak(self, ak: str) -> None:
37 | """
38 | Ak = ak: str
39 |
40 | @param ak: API Key
41 | @return: None
42 | """
43 | self.__ak = ak
44 |
45 | @property
46 | def Sk(self) -> str:
47 | """
48 | Sk -> str
49 |
50 | @return: Secret Key
51 | """
52 | return self.__sk
53 |
54 | @Sk.setter
55 | def Sk(self, sk: str) -> None:
56 | """
57 | Sk = sk: str
58 |
59 | @param sk: Secret Key
60 | @return: None
61 | """
62 | self.__sk = sk
63 |
--------------------------------------------------------------------------------
/inside/Template/Meta_Singleton.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/14
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Meta_Singleton.py
8 | # @Function : 单例基类
9 | from abc import ABCMeta
10 |
11 | __all__ = ['SINGLETON', 'SINGLETON_ABC']
12 |
13 |
14 | class SINGLETON(type):
15 | """单例元类"""
16 | __instances = {}
17 |
18 | def __call__(cls, *args, **kwargs):
19 | """
20 | 重载(),确保返回的为唯一对象
21 |
22 | :param args:
23 | :param kwargs:
24 | :return:
25 | """
26 | if not cls.__instances.get(cls):
27 | cls.__instances[cls] = super(SINGLETON, cls).__call__(*args,
28 | **kwargs)
29 | return cls.__instances[cls]
30 |
31 |
32 | class SINGLETON_ABC(ABCMeta):
33 | """抽象类的单例元类"""
34 | __instances = {}
35 |
36 | def __call__(cls, *args, **kwargs):
37 | """
38 | 重载(),确保返回的为唯一对象
39 |
40 | :param args:
41 | :param kwargs:
42 | :return:
43 | """
44 | if not cls.__instances.get(cls):
45 | cls.__instances[cls] = super(SINGLETON_ABC, cls).__call__(*args,
46 | **kwargs)
47 | return cls.__instances[cls]
48 |
--------------------------------------------------------------------------------
/inside/Login/QR_Vessel.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : QR_Vessel.py
8 | # @Function : 二维码容器
9 | from inside.Driver.Driver_Manage import DRIVER_MANAGE
10 | from inside.Template.Meta_Singleton import SINGLETON
11 |
12 | __all__ = ['QR_VESSEL']
13 |
14 |
15 | class QR_VESSEL(metaclass=SINGLETON):
16 | """二维码容器类"""
17 |
18 | def __init__(self):
19 | """
20 | QR_VESSEL()
21 | 初始化
22 |
23 | """
24 | self.__driver = DRIVER_MANAGE().QR
25 |
26 | def Show_QR(self, qr: str):
27 | """
28 | Show_QR(qr: str) -> None
29 | 显示二维码
30 |
31 | :param qr: 图片二维码,格式为:data:image/png;base64,xxx==
32 | :return: None
33 | """
34 | js = "document.body.innerHTML='';"
35 | js += f'''
36 | var qr = document.createElement("img");
37 | qr.src="{qr}";
38 | document.body.appendChild(qr);'''
39 | self.__driver.execute_script(script=js)
40 |
41 | def QR_QUIT(self) -> str:
42 | """
43 | QR_QUIT() -> str
44 | 二维码容器退出
45 |
46 | :return: str
47 | Success: 退出成功
48 | Nonexistence: 不存在任务浏览器
49 | """
50 | return DRIVER_MANAGE().QR_Quit
51 |
52 | def __del__(self) -> str:
53 | return DRIVER_MANAGE().QR_Quit
54 |
55 |
56 |
--------------------------------------------------------------------------------
/inside/Baidu_AI/Baidu_AI_Manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Baidu_AI_Manage.py
8 | # @Function : 百度AI管理类
9 | from inside.Baidu_AI.Baidu_AI_Verify import BAIDU_AI_Verify
10 | from inside.Baidu_AI.Baidu_AI_Tools import BAIDU_AI_TOOLS
11 | from inside.Template.Meta_Singleton import SINGLETON
12 | from inside.DB.DB_Manage import DB_MANAGE
13 | __all__ = ['BAIDU_AI_MANAGE']
14 |
15 |
16 | class BAIDU_AI_MANAGE(metaclass=SINGLETON):
17 | """百度AI管理类"""
18 |
19 | @classmethod
20 | def Verify(cls) -> None:
21 | """
22 | Verify() -> None
23 | 验证百度AI
24 |
25 | Returns: None
26 |
27 | """
28 | baidu_ai = None
29 | if not DB_MANAGE().Baidu_AI.Empty():
30 | baidu_ai = DB_MANAGE().Baidu_AI.Query()
31 | if BAIDU_AI_Verify.Check_AS(baidu_ai=baidu_ai) != 1:
32 | baidu_ai = None
33 | if not baidu_ai:
34 | baidu_ai = BAIDU_AI_Verify().Verify()
35 | DB_MANAGE().Baidu_AI.Insert(baidu_ai=baidu_ai)
36 |
37 | @classmethod
38 | def Tools(cls) -> BAIDU_AI_TOOLS:
39 | """
40 | Tools() -> BAIDU_AI_TOOLS
41 | 返回百度AI工具
42 |
43 | Returns: BAIDU_AI_TOOLS
44 |
45 | """
46 | baidu_ai = DB_MANAGE().Baidu_AI.Query()
47 | return BAIDU_AI_TOOLS(baidu_ai=baidu_ai)
48 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Intercept/ABC_Answer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : ABC_Answer.py
8 | # @Function : 答题抽象类
9 | from abc import abstractmethod
10 | from typing import Dict
11 |
12 | from inside.Template.Meta_Singleton import SINGLETON_ABC
13 | __all__ = ['ABC_ANSWER']
14 |
15 |
16 | class ABC_ANSWER(metaclass=SINGLETON_ABC):
17 | """答题抽象类"""
18 |
19 | @abstractmethod
20 | def Url_Put(self, url: str) -> bool:
21 | """
22 | Url_Put(url: str) -> bool
23 | 提交判断
24 |
25 | :param url: url
26 | :return: bool
27 | """
28 | pass
29 |
30 | @abstractmethod
31 | def Url_Res(self, url: str) -> bool:
32 | """
33 | Url_Res(url: str) -> bool
34 | 答案判断
35 |
36 | :param url: url
37 | :return: bool
38 | """
39 | pass
40 |
41 | @abstractmethod
42 | def Extract(self, data: bytes) -> Dict:
43 | """
44 | Extract(data: bytes) -> Dict
45 | 提取答案
46 |
47 | :param data: 包含答案的字节数据
48 | :return: Dict
49 | """
50 | pass
51 |
52 | @abstractmethod
53 | def Inject(self, data: bytes, res: Dict) -> bytes:
54 | """
55 | Inject(data: bytes, res: Dict) -> bytes
56 | 注入答案
57 |
58 | :param data: 原始提交数据
59 | :param res: 包含答案数据
60 | :return: 修改后的提交数据
61 | """
62 | pass
63 |
--------------------------------------------------------------------------------
/inside/Tools/Random.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Random.py
8 | # @Function : 随机数
9 | import random
10 | import time
11 | from typing import Union
12 |
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['RANDOM', 'Int', 'Float']
16 |
17 |
18 | class RANDOM(metaclass=SINGLETON):
19 | """
20 | 提供随机数
21 | """
22 | random.seed(time.time())
23 |
24 | @classmethod
25 | def Int(cls, a: int, b: int) -> int:
26 | """
27 | Int(a: int, b: int) -> int
28 | 类方法
29 | 输出随机整数,a<=结果<=b
30 |
31 | :param a: 起始数
32 | :param b: 结束数
33 | :return: int
34 | """
35 | return random.randint(a=a, b=b)
36 |
37 | # 浮点数
38 | @classmethod
39 | def Float(cls, a: Union[float, int], b: Union[float, int]) -> float:
40 | """
41 | Float(a: float, b: float) -> float
42 | 类方法
43 | 输出随机浮点数,a<=结果<=b
44 |
45 | :param a: 起始数
46 | :param b: 结束数
47 | :return: float
48 | """
49 | return random.uniform(a=a, b=b)
50 |
51 | @classmethod
52 | def Float_Precision(cls, a: Union[float, int], b: Union[float, int],
53 | precision: int):
54 | return round(number=cls.Float(a=a, b=b), ndigits=precision)
55 |
56 |
57 | _inst = RANDOM()
58 | Int = _inst.Int
59 | Float = _inst.Float
60 | Float_Precision = _inst.Float_Precision
61 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/User.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : User.py
8 | # @Function : 用户类
9 | from datetime import datetime
10 | from typing import Dict
11 |
12 | __all__ = ['USER']
13 |
14 |
15 | class USER(object):
16 | """用户类"""
17 |
18 | def __init__(self, user_id: int, token: str):
19 | """
20 | USER(user_id: int, token: str)
21 | 初始化
22 |
23 | :param user_id: 用户ID
24 | :param token: 令牌
25 | """
26 | self.__user_id = user_id
27 | self.__token = token
28 | self.__time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
29 |
30 | @property
31 | def Id(self) -> int:
32 | """
33 | Id -> int
34 | 名称
35 |
36 | :return: int
37 | """
38 | return self.__user_id
39 |
40 | @property
41 | def Token(self) -> str:
42 | """
43 | Token -> str
44 | 令牌
45 |
46 | :return: str
47 | """
48 | return self.__token
49 |
50 | @property
51 | def Token_Driver(self) -> Dict:
52 | """
53 | Token_Driver -> Dict
54 | 驱动令牌,格式为:{'domain': '.xuexi.cn', 'name': 'token', 'value': 'token'}
55 |
56 | :return: Dict
57 | """
58 | return {'domain': '.xuexi.cn', 'name': 'token', 'value': self.Token}
59 |
60 | @property
61 | def Time(self) -> str:
62 | """
63 | Time -> str
64 | 对象创建时间戳
65 |
66 | :return: str
67 | """
68 | return self.__time
69 |
--------------------------------------------------------------------------------
/inside/Template/ABC_Driver_Analysis.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/17
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : ABC_Driver_Analysis.py
8 | # @Function : 驱动解析基类
9 | from abc import abstractmethod
10 | from urllib.parse import ParseResult
11 |
12 | from inside.Config.System import SYSTEM
13 | from inside.Template.Meta_Singleton import SINGLETON_ABC
14 |
15 | __all__ = ['DRIVER_ANALYSIS']
16 |
17 |
18 | class ABC_DRIVER_ANALYSIS(metaclass=SINGLETON_ABC):
19 | """驱动解析抽象类"""
20 | @abstractmethod
21 | def Master(self) -> ParseResult:
22 | """
23 | Url() -> ParseResult
24 | 驱动集合链接
25 |
26 | :return: ParseResult
27 | """
28 | pass
29 |
30 | @abstractmethod
31 | def Download(self, system: SYSTEM) -> ParseResult:
32 | """
33 | Download() -> ParseResult
34 | 驱动下载链接
35 |
36 | :param system: 系统
37 | :param version: 版本号
38 | :return: ParseResult
39 | """
40 | pass
41 |
42 |
43 | class DRIVER_ANALYSIS(ABC_DRIVER_ANALYSIS):
44 | """驱动解析类实际继承类"""
45 | _map = {
46 | 'linux': 'linux',
47 | 'win32': 'win',
48 | 'darwin': 'mac'
49 | }
50 |
51 | @property
52 | def Master(self) -> ParseResult:
53 | """
54 | Url -> ParseResult
55 | 驱动集合链接
56 |
57 | :return: ParseResult
58 | """
59 | pass
60 |
61 | def Download(self, system: SYSTEM) -> ParseResult:
62 | """
63 | Download -> ParseResult
64 | 驱动下载链接
65 |
66 | :param system: 系统
67 | :param version: 版本号
68 | :return: ParseResult
69 | """
70 | pass
71 |
72 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Intercept/Script.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/26
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Intercept11.py
8 | # @Function : 答题拦截注入脚本
9 | import os
10 | import sys
11 | from json import JSONDecodeError
12 | from typing import Dict
13 |
14 | """此处是因为脚本加载Intercept失败,所做的增加搜索路径"""
15 | sys.path.append(
16 | os.path.dirname(
17 | os.path.dirname(
18 | os.path.dirname(
19 | os.path.dirname(
20 | os.path.dirname(os.path.abspath(__file__))
21 | )
22 | )
23 | )
24 | )
25 | )
26 | from inside.Task.Mitmdump.Intercept.ABC_Answer import ABC_ANSWER
27 | from inside.Task.Mitmdump.Intercept.Daily import DAILY
28 | from inside.Task.Mitmdump.Intercept.Project import PROJECT
29 | from inside.Task.Mitmdump.Intercept.Weekly import WEEKLY
30 |
31 | from mitmproxy.http import HTTPFlow
32 |
33 | gears = (DAILY(), WEEKLY(), PROJECT())
34 | gear: ABC_ANSWER = None
35 | put_data: Dict
36 |
37 |
38 | def request(up: HTTPFlow) -> None:
39 | if gear:
40 | if gear.Url_Put(url=up.request.url):
41 | print("检测到提交,注入中")
42 | try:
43 | up.request.content = gear.Inject(
44 | data=up.request.content,
45 | res=put_data
46 | )
47 | print("注入成功")
48 | except JSONDecodeError:
49 | print("检测到本次提交为干扰")
50 |
51 |
52 | def response(down: HTTPFlow):
53 | for g in gears:
54 | if g.Url_Res(url=down.request.url):
55 | try:
56 | global put_data, gear
57 | put_data = g.Extract(data=down.response.content)
58 | gear = g
59 | print("抓取答案完成")
60 | except JSONDecodeError:
61 | pass
62 |
--------------------------------------------------------------------------------
/inside/DB/DB_Project.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/4
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Project.py
8 | # @Function : 专项答题表操作
9 | from sqlite3 import Connection, Cursor
10 |
11 | from inside.DB.DB_Config import DB_CONFIG
12 | from inside.DB.Table_Class.Project import PROJECT
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['DB_PROJECT']
16 |
17 |
18 | class DB_PROJECT(metaclass=SINGLETON):
19 | """专项答题表操作类"""
20 |
21 | def __init__(self, connect: Connection, cursor: Cursor):
22 | """
23 | 初始化
24 |
25 | :param connect: 数据库连接对象
26 | :param cursor: 数据库光标
27 | """
28 | self.__connect = connect
29 | self.__cursor = cursor
30 | self.__name = DB_CONFIG().Project
31 | self.__fields = DB_CONFIG().Project_Fields
32 |
33 | def Insert(self, project: PROJECT) -> None:
34 | """
35 | Insert(project: PROJECT) -> None
36 | 插入专项答题id,如已存在则不进行操作
37 |
38 | :param project: 专项答题
39 | :return: None
40 | """
41 | if not self.Exist(project=project):
42 | sql = f'''INSERT INTO
43 | {self.__name} (
44 | {self.__fields[1]})
45 | VALUES (?);'''
46 | data = (project.Id, )
47 | self.__cursor.execute(sql, data)
48 | self.__connect.commit()
49 |
50 | def Exist(self, project: PROJECT) -> bool:
51 | """
52 | Exist(project: PROJECT) -> bool
53 | 是否存在指定id
54 |
55 | :param project: 专项答题
56 | :return: bool
57 | """
58 | sql = f'''SELECT 1 FROM {self.__name}
59 | WHERE {self.__fields[1]}=?;'''
60 | data = (project.Id,)
61 | result = self.__cursor.execute(sql, data)
62 | try:
63 | next(result)
64 | return True
65 | except StopIteration:
66 | return False
67 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/Task.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task.py
8 | # @Function : 任务类
9 | from typing import Union
10 |
11 | __all__ = ['TASK']
12 |
13 |
14 | class TASK(object):
15 | """任务类"""
16 |
17 | def __init__(self, link: str, isread: Union[int, bool]):
18 | """
19 | TASK(link: str, isread: Union[int, bool])
20 | 初始化
21 |
22 | :param link: 链接
23 | :param isread: 是否已读
24 | """
25 |
26 | self.__link = link
27 | self.__isread = self.__Is_Read(isread=isread)
28 |
29 | @property
30 | def Link(self) -> str:
31 | """
32 | Link -> str
33 | 链接
34 |
35 | :return: str
36 | """
37 | return self.__link
38 |
39 | @property
40 | def Is_Read(self) -> bool:
41 | """
42 | Is_Read -> bool
43 | 是否已读
44 |
45 | :return: bool
46 | """
47 | return self.__isread
48 |
49 | @Is_Read.setter
50 | def Is_Read(self, isread: Union[int, bool]) -> None:
51 | """
52 | Is_Read -> None
53 | 设置是否已读
54 |
55 | :param isread: Union[int, bool]
56 | :return: None
57 | """
58 | self.__isread = self.__Is_Read(isread=isread)
59 |
60 | @classmethod
61 | def __Is_Read(cls, isread: Union[int, bool]) -> bool:
62 | """
63 | __Is_Read(isread: Union[int, bool]) -> bool
64 | 设定是否已读,由于数据库内不能存储布尔值,所以由0/1代替;
65 | 因此数字只限定0/1
66 |
67 | :param isread: 1/0 or bool
68 | :return: bool
69 | """
70 | if isinstance(isread, bool):
71 | return isread
72 | elif isinstance(isread, int):
73 | if isread in (0, 1):
74 | return True if isread else False
75 | else:
76 | raise Exception('isread must be 0 or 1')
77 |
78 | @property
79 | def Is_Read_DB(self) -> int:
80 | """
81 | Is_Read_DB -> int
82 | 是否已读(数据库读取接口,返回1/0)
83 |
84 | :return: int
85 | """
86 | return 1 if self.__isread else 0
87 |
--------------------------------------------------------------------------------
/inside/Baidu_AI/Baidu_AI_Verify.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Baidu_AI_Verify.py
8 | # @Function : 验证百度AI
9 | import requests
10 |
11 | from inside.DB.Table_Class.Baidu_AI import BAIDU_AI
12 | from inside.Template.Meta_Singleton import SINGLETON
13 | __all__ = ['BAIDU_AI_Verify']
14 |
15 |
16 | class BAIDU_AI_Verify(metaclass=SINGLETON):
17 | """百度AI验证类"""
18 |
19 | def Verify(self) -> BAIDU_AI:
20 | """
21 | Verify() -> BAIDU_AI
22 | 验证
23 |
24 | Returns: BAIDU_AI
25 |
26 | """
27 | ak = input("请输入API Key:").strip()
28 | sk = input("请输入Secret Key:").strip()
29 | baidu_ai = BAIDU_AI(ak=ak, sk=sk)
30 | while True:
31 | temp = self.Check_AS(baidu_ai=baidu_ai)
32 | if temp == 1:
33 | break
34 | elif temp == -1:
35 | print('API Key错误')
36 | baidu_ai.Ak = input("请重新输入API Key:").strip()
37 | continue
38 | elif temp == -2:
39 | print('Secret Key错误')
40 | baidu_ai.Sk = input("请重新输入Secret Key:").strip()
41 | continue
42 | return baidu_ai
43 |
44 | @classmethod
45 | def Check_AS(cls, baidu_ai: BAIDU_AI) -> int:
46 | """
47 | Check_AS(baidu_ai: BAIDU_AI) -> int
48 | 检查API Key、Secret Key是否有效
49 |
50 | Args:
51 | baidu_ai: 百度AI
52 |
53 | Returns:
54 | 1: 有效
55 | -1: API Key 无效
56 | -2: Secret Key 无效
57 |
58 | """
59 | host = f'https://aip.baidubce.com/oauth/2.0/token?grant_type' \
60 | f'=client_credentials&client_id={baidu_ai.Ak}&client_' \
61 | f'secret={baidu_ai.Sk}'
62 | html = requests.get(host)
63 | if html.json().get('access_token'):
64 | return 1
65 | elif html.json().get('error_description') == 'unknown client id':
66 | return -1
67 | elif html.json().get('error_description') == 'Client authentication ' \
68 | 'failed':
69 | return -2
70 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Intercept/Daily.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Daily.py
8 | # @Function :
9 | import base64
10 | import json
11 | from random import randint
12 | from typing import Dict
13 |
14 | from inside.Task.Mitmdump.Intercept.ABC_Answer import ABC_ANSWER
15 | __all__ = ['DAILY']
16 |
17 |
18 | class DAILY(ABC_ANSWER):
19 | """每日答题"""
20 | def Url_Put(self, url: str) -> bool:
21 | """
22 | Url_Put(url: str) -> bool
23 | 提交判断
24 |
25 | :param url: url
26 | :return: bool
27 | """
28 | return "https://pc-proxy-api.xuexi.cn/api/exam/service/practice" \
29 | "/quizSubmitV3" == url
30 |
31 | def Url_Res(self, url: str) -> bool:
32 | """
33 | Url_Res(url: str) -> bool
34 | 答案判断
35 |
36 | :param url: url
37 | :return: bool
38 | """
39 | return "https://pc-proxy-api.xuexi.cn/api/exam/service/common" \
40 | "/deduplicateRandomSearchV3?limit=5&activityCode=QUIZ_ALL" \
41 | "&forced=true" == url
42 |
43 | def Extract(self, data: bytes) -> Dict:
44 | """
45 | Extract(data: bytes) -> Dict
46 | 提取答案
47 |
48 | :param data: 包含答案的字节数据
49 | :return: Dict
50 | """
51 | data = data.decode('utf-8')
52 | data = json.loads(data)['data_str']
53 | return json.loads(base64.b64decode(data).decode('utf-8'))
54 |
55 | def Inject(self, data: bytes, res: Dict) -> bytes:
56 | """
57 | Inject(data: bytes, res: Dict) -> bytes
58 | 注入答案
59 |
60 | :param data: 原始提交数据
61 | :param res: 包含答案数据
62 | :return: 修改后的提交数据
63 | """
64 | data = json.loads(data.decode('utf-8'))
65 | data['uniqueId'] = res['uniqueId']
66 | data['questions'].clear()
67 | data['usedTime'] = randint(20, 50)
68 | for question in res['questions']:
69 | data['questions'].append(
70 | {
71 | 'questionId': question['questionId'],
72 | 'answers': question['correct'],
73 | 'correct': True
74 | }
75 | )
76 | return json.dumps(data).encode('utf-8')
77 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Intercept/Weekly.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Weekly.py
8 | # @Function :
9 | import base64
10 | import json
11 | import re
12 | from random import randint
13 | from typing import Dict
14 |
15 | from inside.Task.Mitmdump.Intercept.ABC_Answer import ABC_ANSWER
16 | __all__ = ['WEEKLY']
17 |
18 |
19 | class WEEKLY(ABC_ANSWER):
20 | """每周答题拦截体"""
21 |
22 | def Url_Put(self, url: str) -> bool:
23 | """
24 | Url_Put(url: str) -> bool
25 | 提交判断
26 |
27 | :param url: url
28 | :return: bool
29 | """
30 | return "https://pc-proxy-api.xuexi.cn/api/exam/service/practice" \
31 | "/weekPracticeSubmitV3" == url
32 |
33 | def Url_Res(self, url: str) -> bool:
34 | """
35 | Url_Res(url: str) -> bool
36 | 答案判断
37 |
38 | :param url: url
39 | :return: bool
40 | """
41 | res = "https://pc-proxy-api.xuexi.cn/api/exam/service/detail/queryV3" \
42 | "\\?type=2&id=.*&forced=true"
43 | if re.match(res, url):
44 | return True
45 | return False
46 |
47 | def Extract(self, data: bytes) -> Dict:
48 | """
49 | Extract(data: bytes) -> Dict
50 | 提取答案
51 |
52 | :param data: 包含答案的字节数据
53 | :return: Dict
54 | """
55 | data = data.decode('utf-8')
56 | data = json.loads(data)['data_str']
57 | return json.loads(base64.b64decode(data).decode('utf-8'))
58 |
59 | def Inject(self, data: bytes, res: Dict) -> bytes:
60 | """
61 | Inject(data: bytes, res: Dict) -> bytes
62 | 注入答案
63 |
64 | :param data: 原始提交数据
65 | :param res: 包含答案数据
66 | :return: 修改后的提交数据
67 | """
68 | data = json.loads(data.decode('utf-8'))
69 | data['uniqueId'] = res['uniqueId']
70 | data['questions'].clear()
71 | data['usedTime'] = randint(20, 50)
72 | for question in res['questions']:
73 | data['questions'].append(
74 | {
75 | 'questionId': question['questionId'],
76 | 'answers': question['correct'],
77 | 'correct': True
78 | }
79 | )
80 | return json.dumps(data).encode('utf-8')
81 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/Video.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Video.py
8 | # @Function : 视频类
9 | from typing import Union
10 |
11 | __all__ = ['VIDEO']
12 |
13 |
14 | class VIDEO(object):
15 | """视频类"""
16 |
17 | def __init__(self, item: str, link: str, isread: Union[int, bool]):
18 | """
19 | VIDEO(item: str, link: str, isread: Union[int, bool])
20 | 初始化
21 |
22 | :param item: 标识
23 | :param link: 链接
24 | :param isread: 是否已读
25 | """
26 | self.__item = item
27 | self.__link = link
28 | self.__isread = self.__Is_Read(isread=isread)
29 |
30 | @property
31 | def Item(self) -> str:
32 | """
33 | Item -> str
34 | 标识
35 |
36 | :return: str
37 | """
38 | return self.__item
39 |
40 | @property
41 | def Link(self) -> str:
42 | """
43 | Link -> str
44 | 链接
45 |
46 | :return: str
47 | """
48 | return self.__link
49 |
50 | def __Is_Read(self, isread: Union[int, bool]) -> bool:
51 | """
52 | __Is_Read(isread: Union[int, bool]) -> bool
53 | 设定是否已读,由于数据库内不能存储布尔值,所以由0/1代替;
54 | 因此数字只限定0/1
55 |
56 | :param isread: 1/0 or bool
57 | :return: bool
58 | """
59 | if isinstance(isread, bool):
60 | return isread
61 | elif isinstance(isread, int):
62 | if isread in (0, 1):
63 | return True if isread else False
64 | else:
65 | raise Exception('isread must be 0 or 1')
66 |
67 | @property
68 | def Is_Read(self) -> bool:
69 | """
70 | Is_Read -> bool
71 | 是否已读
72 |
73 | :return: bool
74 | """
75 | return self.__isread
76 |
77 | @Is_Read.setter
78 | def Is_Read(self, isread: Union[int, bool]) -> None:
79 | """
80 | Is_Read -> None
81 | 设置是否已读
82 |
83 | :param isread: Union[int, bool]
84 | :return: None
85 | """
86 | self.__isread = self.__Is_Read(isread=isread)
87 |
88 | @property
89 | def Is_Read_DB(self) -> int:
90 | """
91 | Is_Read_DB -> int
92 | 是否已读(数据库读取接口,返回1/0)
93 |
94 | :return: int
95 | """
96 | return 1 if self.__isread else 0
97 |
--------------------------------------------------------------------------------
/inside/DB/Table_Class/Article.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Article_Video.py
8 | # @Function : 文章类
9 | from typing import Union
10 |
11 | __all__ = ['ARTICLE']
12 |
13 |
14 | class ARTICLE(object):
15 | """文章类"""
16 |
17 | def __init__(self, item: str, link: str, isread: Union[int, bool]):
18 | """
19 | ARTICLE(item: str, link: str, isread: Union[int, bool])
20 | 初始化
21 |
22 | :param item: 标识
23 | :param link: 链接
24 | :param isread: 是否已读
25 | """
26 | self.__item = item
27 | self.__link = link
28 | self.__isread = self.__Is_Read(isread=isread)
29 |
30 | @property
31 | def Item(self) -> str:
32 | """
33 | Item -> str
34 | 标识
35 |
36 | :return: str
37 | """
38 | return self.__item
39 |
40 | @property
41 | def Link(self) -> str:
42 | """
43 | Link -> str
44 | 链接
45 |
46 | :return: str
47 | """
48 | return self.__link
49 |
50 | @classmethod
51 | def __Is_Read(cls, isread: Union[int, bool]) -> bool:
52 | """
53 | __Is_Read(isread: Union[int, bool]) -> bool
54 | 设定是否已读,由于数据库内不能存储布尔值,所以由0/1代替;
55 | 因此数字只限定0/1
56 |
57 | :param isread: 1/0 or bool
58 | :return: bool
59 | """
60 | if isinstance(isread, bool):
61 | return isread
62 | elif isinstance(isread, int):
63 | if isread in (0, 1):
64 | return True if isread else False
65 | else:
66 | raise Exception('isread must be 0 or 1')
67 |
68 | @property
69 | def Is_Read(self) -> bool:
70 | """
71 | Is_Read -> bool
72 | 是否已读
73 |
74 | :return: bool
75 | """
76 | return self.__isread
77 |
78 | @Is_Read.setter
79 | def Is_Read(self, isread: Union[int, bool]) -> None:
80 | """
81 | Is_Read -> None
82 | 设置是否已读
83 |
84 | :param isread: Union[int, bool]
85 | :return: None
86 | """
87 | self.__isread = self.__Is_Read(isread=isread)
88 |
89 | @property
90 | def Is_Read_DB(self) -> int:
91 | """
92 | Is_Read_DB -> int
93 | 是否已读(数据库读取接口,返回1/0)
94 |
95 | :return: int
96 | """
97 | return 1 if self.__isread else 0
98 |
--------------------------------------------------------------------------------
/inside/Driver/Driver_Check.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/16
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Driver_Check.py
8 | # @Function : 驱动检测
9 | import os
10 |
11 | from inside.Config.Path import PATH
12 | from inside.Config.System import SYSTEM
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['DRIVER_CHECK']
16 |
17 |
18 | class DRIVER_CHECK(metaclass=SINGLETON):
19 | """驱动检测类"""
20 | @property
21 | def Dir(self) -> bool:
22 | """
23 | Dir -> bool
24 | 驱动目录是否存在
25 |
26 | :return: bool
27 | """
28 | return os.path.exists(PATH().Driver)
29 |
30 | @property
31 | def File(self) -> bool:
32 | """
33 | File -> bool
34 | 驱动文件是否存在
35 |
36 | :return: bool
37 | """
38 | return os.path.exists(PATH().Driver_File)
39 |
40 | @property
41 | def Version(self) -> str:
42 | """
43 | Version -> str
44 | 驱动支持谷歌浏览器版本号
45 |
46 | :return: str
47 | """
48 | cmd = PATH().Driver_File + ' --version'
49 | version = os.popen(cmd=cmd).readline()
50 | version = version.strip().split()
51 | return version[1]
52 |
53 | @property
54 | def Execute_Permission(self) -> bool:
55 | """
56 | Execute_Permission -> bool
57 | 驱动程序是否有执行权限
58 |
59 | :return: bool
60 | """
61 | try:
62 | isinstance(self.Version, str)
63 | return True
64 | except IndexError:
65 | return False
66 |
67 | def Driver_Chrome_Version(self, system: SYSTEM) -> bool:
68 | """
69 | Driver_Chrome_Version(system: SYSTEM) -> bool
70 | 驱动程序是否支持谷歌浏览器版本
71 |
72 | :param system: 系统类
73 | :return: bool
74 | """
75 | driver_version = self.Version.split('.')[:3]
76 | chrome_version = system.Chrome_Version.split('.')[:3]
77 | return driver_version == chrome_version
78 |
79 | def Add_Execute_Permission(self, system: SYSTEM) -> bool:
80 | """
81 | Add_Execute_Permission -> bool
82 | 为驱动程序添加执行权限
83 |
84 | :param system: 系统类
85 | :return: bool
86 | """
87 | command = system.Driver_Chmod+PATH().Driver_File
88 | if command != PATH().Driver_File:
89 | return os.system(command=command) == 0
90 | return True
91 |
92 |
--------------------------------------------------------------------------------
/inside/Task/Task_Article_Video.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/25
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Article_Video.py
8 | # @Function : 文章视频任务
9 | import time
10 | from typing import Union
11 |
12 | from tqdm import tqdm
13 | from selenium.webdriver.chrome.webdriver import WebDriver
14 |
15 | from inside.Config.Api import API
16 | from inside.DB.Table_Class.Article import ARTICLE
17 | from inside.DB.Table_Class.Video import VIDEO
18 | from inside.Template.Meta_Singleton import SINGLETON
19 | from inside.Tools.Network import NETWORK
20 | from inside.Tools.Random import RANDOM
21 |
22 | __all__ = ['TASK_ARTICLE_VIDEO']
23 |
24 |
25 | class TASK_ARTICLE_VIDEO(metaclass=SINGLETON):
26 |
27 | def __init__(self, task_driver: WebDriver):
28 | """
29 | TASK_ARTICLE_VIDEO(task_driver: WebDriver)
30 | 初始化
31 |
32 | :param task_driver: 驱动
33 | """
34 | self.__driver = task_driver
35 | self.__network = NETWORK()
36 |
37 | def __Check(self, item: str, tq: int) -> None:
38 | """
39 | __Check(item: str, tq: int) -> None
40 | 监测任务的完成
41 |
42 | :param item: 任务编号
43 | :param tq: 等待程度
44 | :return: None
45 | """
46 | bar = tqdm(
47 | desc=item,
48 | total=tq,
49 | unit='it',
50 | leave=False,
51 | ncols=70
52 | )
53 | count = 0
54 | start = time.time()
55 | while True:
56 | js = "document.documentElement.scrollTop={speed}"
57 | self.__driver.execute_script(
58 | script=js.format(speed=RANDOM.Int(a=1, b=10))
59 | )
60 | temp = self.__network.Get()
61 | if API().Task_Check.geturl() in temp.keys() or \
62 | time.time() - start >= 35:
63 | count += 1
64 | bar.update(n=1)
65 | start = time.time()
66 | if count >= tq:
67 | break
68 | time.sleep(0.1)
69 | bar.close()
70 |
71 | def Do(self, task: Union[ARTICLE, VIDEO], tq: int) -> None:
72 | """
73 | Do(task: Union[ARTICLE, VIDEO]) -> None
74 | 进行一次文章或视频任务
75 |
76 | :param task: 任务
77 | :param tq: 等待程度
78 | :return: None
79 | """
80 | self.__network.Clear()
81 | self.__driver.get(task.Link)
82 | self.__Check(item=task.Item, tq=tq)
83 |
--------------------------------------------------------------------------------
/inside/Driver/Driver_Download.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/18
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Driver_Download.py
8 | # @Function : 驱动下载
9 | import os
10 | import zipfile
11 | from urllib.parse import ParseResult
12 | from urllib.request import urlopen
13 |
14 | import requests
15 | import shutil
16 | from tqdm import tqdm
17 |
18 | from inside.Config.Path import PATH
19 | from inside.Template.Meta_Singleton import SINGLETON
20 |
21 | __all__ = ['DRIVER_DOWNLOAD']
22 |
23 |
24 | class DRIVER_DOWNLOAD(metaclass=SINGLETON):
25 | """驱动下载类"""
26 |
27 | def __Download(self, link: ParseResult, dst: str) -> int:
28 | """
29 | __Download(link: ParseResult, dst: str) -> str
30 | 驱动下载本体
31 |
32 | :param link: 下载链接
33 | :param dst: 保存路径及文件名
34 | :return: str
35 | """
36 | size = int(urlopen(link.geturl()).info().get('Content-Length', -1))
37 | bar = tqdm(
38 | total=size,
39 | initial=0,
40 | unit='B',
41 | unit_scale=True,
42 | desc=os.path.basename(dst),
43 | ncols=70
44 | )
45 | req = requests.get(url=link.geturl(), stream=True)
46 | with open(dst, 'ab') as f:
47 | for chunk in req.iter_content(chunk_size=1024):
48 | if chunk:
49 | f.write(chunk)
50 | bar.update(len(chunk))
51 | else:
52 | f.close()
53 | bar.close()
54 | return size
55 |
56 | def __Unzip(self, dst: str) -> bool:
57 | """
58 | __Unzip(dst: str) -> bool
59 | 解压zip文件
60 |
61 | :param dst: 解压路径
62 | :return:
63 | """
64 | if zipfile.is_zipfile(dst):
65 | files = zipfile.ZipFile(dst)
66 | if not files.testzip():
67 | files.extractall(path=PATH().Driver)
68 | return True
69 | return False
70 |
71 | def Download(self, link: ParseResult) -> int:
72 | """
73 | Download(link: ParseResult) -> int
74 | 驱动下载
75 |
76 | :param link: 驱动下载链接
77 | :return: int 文件大小, 默认单位B
78 | """
79 | suffix = os.path.basename(link.path)
80 | zip_file = os.path.join(PATH().Driver, suffix)
81 | shutil.rmtree(path=PATH().Driver)
82 | os.mkdir(PATH().Driver)
83 | temp = self.__Download(link=link, dst=zip_file)
84 | self.__Unzip(dst=zip_file)
85 | return temp
86 |
--------------------------------------------------------------------------------
/inside/Info/Task_Info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/23
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Info.py
8 | # @Function : 任务信息类
9 | __all__ = ['TASK_INFO']
10 |
11 |
12 | class TASK_INFO(object):
13 | """任务信息类"""
14 |
15 | def __init__(self, ruleId: int, name: str, desc: str, currentScore: int,
16 | dayMaxScore: int):
17 | """
18 | TASK_INFO(ruleId: int, name: str, desc: str, currentScore: int,
19 | dayMaxScore: int)
20 | 初始化
21 |
22 | :param ruleId: ID
23 | :param name: 名称
24 | :param desc: 说明
25 | :param currentScore: 已获积分
26 | :param dayMaxScore: 最大积分
27 | """
28 | self.__rule_id = ruleId
29 | self.__name = name
30 | self.__desc = desc
31 | self.__current_score = currentScore
32 | self.__day_max_score = dayMaxScore
33 |
34 | @property
35 | def Rule_Id(self) -> int:
36 | """
37 | Rule_Id -> int
38 | Id
39 |
40 | :return: int
41 | """
42 | return self.__rule_id
43 |
44 | @property
45 | def Name(self) -> str:
46 | """
47 | Name -> str
48 | 名称
49 |
50 | :return: str
51 | """
52 | return self.__name
53 |
54 | @property
55 | def Desc(self) -> str:
56 | """
57 | Desc -> str
58 | 说明
59 |
60 | :return: str
61 | """
62 | return self.__desc
63 |
64 | @property
65 | def Current_Score(self) -> int:
66 | """
67 | Current_Score -> int
68 | 已获积分
69 |
70 | :return: int
71 | """
72 | return self.__current_score
73 |
74 | @Current_Score.setter
75 | def Current_Score(self, currentScore: int) -> None:
76 | """
77 | Current_Score -> None
78 | 设置已获积分
79 |
80 | :param currentScore: 更新积分
81 | :return: None
82 | """
83 | self.__current_score = currentScore
84 |
85 | @property
86 | def Day_Max_Score(self) -> int:
87 | """
88 | Day_Max_Score -> int
89 | 最大积分
90 |
91 | :return: int
92 | """
93 | return self.__day_max_score
94 |
95 | @property
96 | def Difference_Score(self) -> int:
97 | """
98 | Difference_Score -> int
99 | 剩余可获积分
100 |
101 | :return: int
102 | """
103 | return self.Day_Max_Score - self.Current_Score
104 |
--------------------------------------------------------------------------------
/inside/Tools/Requests.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/21
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Requests.py
8 | # @Function : requests初始化
9 | from typing import Dict
10 |
11 | import requests
12 | import urllib3
13 | from requests import Response
14 |
15 | from inside.Config.User_Agent import USER_AGENT
16 | from inside.Template.Meta_Singleton import SINGLETON
17 |
18 | __all__ = ['REQUESTS']
19 |
20 |
21 | class REQUESTS(metaclass=SINGLETON):
22 | """requests重写类,拥有统一user-agent"""
23 |
24 | __header = {
25 | 'User-Agent': USER_AGENT().User_Agent
26 | }
27 | __proxies = {
28 | 'http': 'http://127.0.0.1:8080',
29 | 'https': 'http://127.0.0.1:8080'
30 | }
31 | urllib3.disable_warnings()
32 |
33 | @classmethod
34 | def __Check_Headers(cls, **kwargs) -> Dict:
35 | """
36 | __Check_Headers(**kwargs) -> Dict
37 | 检测headers内容进行更新
38 |
39 | :param kwargs: 关键字参数
40 | :return: Dict
41 | """
42 | if kwargs.get('headers'):
43 | headers = kwargs.pop('headers')
44 | cls.__header.update(headers)
45 | return kwargs
46 |
47 | @classmethod
48 | def Get(cls, url: str, params=None, **kwargs) -> Response:
49 | """
50 | Get(url: str, params=None, **kwargs) -> Response
51 | Get请求
52 |
53 | :param url: 链接
54 | :param params: 参数
55 | :param kwargs: 关键字参数
56 | :return: Response
57 | """
58 | kwargs = cls.__Check_Headers(**kwargs)
59 | html = requests.get(
60 | url=url,
61 | headers=cls.__header,
62 | proxies=cls.__proxies,
63 | verify=False,
64 | params=params,
65 | **kwargs
66 | )
67 | html.encoding = html.apparent_encoding
68 | return html
69 |
70 | @classmethod
71 | def Post(cls, url: str, data=None, json=None, **kwargs) -> Response:
72 | """
73 | Post(url: str, data=None, json=None, **kwargs) -> Response
74 | Post请求
75 |
76 | :param url:链接
77 | :param data: 数据
78 | :param json: 数据
79 | :param kwargs: 参数
80 | :return: Response
81 | """
82 | kwargs = cls.__Check_Headers(**kwargs)
83 | html = requests.post(
84 | url=url,
85 | headers=cls.__header,
86 | proxies=cls.__proxies,
87 | verify=False,
88 | data=data,
89 | json=json, **kwargs
90 | )
91 | html.encoding = html.apparent_encoding
92 | return html
93 |
--------------------------------------------------------------------------------
/inside/Config/Path.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/14
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Path.py
8 | # @Function : 路径配置文件
9 | import os
10 |
11 | from inside.Config.System import SYSTEM
12 | from inside.Template.Meta_Singleton import SINGLETON
13 |
14 | __all__ = ['PATH']
15 |
16 |
17 | class PATH(metaclass=SINGLETON):
18 | """路径类"""
19 |
20 | def __init__(self):
21 | """
22 | PATH()
23 | 初始化,配置文件绝对路径
24 |
25 | """
26 | self.__Config_Path: str = os.path.dirname(os.path.abspath(__file__))
27 |
28 | @property
29 | def Base(self) -> str:
30 | """
31 | Base -> str
32 | 项目绝对路径
33 |
34 | :return: str
35 | """
36 | return os.path.dirname(os.path.dirname(self.__Config_Path))
37 |
38 | @property
39 | def Driver(self) -> str:
40 | """
41 | Driver -> str
42 | 驱动目录绝对路径
43 |
44 | :return: str
45 | """
46 | return os.path.join(self.Base, 'Driver')
47 |
48 | @property
49 | def Driver_File(self) -> str:
50 | """
51 | Driver_File -> str
52 | 驱动文件绝对路径
53 |
54 | :return: str
55 | """
56 | return os.path.join(self.Driver, SYSTEM().Driver)
57 |
58 | @property
59 | def DB(self) -> str:
60 | """
61 | DB -> str
62 | 数据库目录绝对路径
63 |
64 | :return: str
65 | """
66 | return os.path.join(self.Base, 'DB')
67 |
68 | @property
69 | def DB_File(self) -> str:
70 | """
71 | DB_File -> str
72 | 数据库文件绝对路径
73 |
74 | :return: str
75 | """
76 | return os.path.join(self.DB, 'db.db')
77 |
78 | @property
79 | def Temp(self) -> str:
80 | """
81 | Temp -> str
82 | 临时文件绝对路径
83 |
84 | :return: str
85 | """
86 | return os.path.join(self.Base, 'Temp')
87 |
88 | @property
89 | def Script(self) -> str:
90 | """
91 | Script -> str
92 | 拦截注入脚本绝对路径
93 |
94 | :return: str
95 | """
96 | temp = os.path.join(os.path.dirname(self.__Config_Path), 'Task')
97 | temp = os.path.join(temp, 'Mitmdump')
98 | temp = os.path.join(temp, 'Intercept')
99 | temp = os.path.join(temp, 'Script.py')
100 | return temp
101 |
102 | @property
103 | def Baidu_AI_On(self) -> str:
104 | """
105 | 百度AI开关共享文件
106 |
107 | Returns: str
108 |
109 | """
110 | return os.path.join(os.path.dirname(self.Script), 'on')
111 |
--------------------------------------------------------------------------------
/inside/Info/Info_Manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/23
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Info_Manage.py
8 | # @Function : 信息管理
9 | from typing import Dict, Tuple
10 |
11 | from inside.Info.Get_Info import GET_INFO
12 | from inside.Info.Task_Info import TASK_INFO
13 | from inside.Info.User_Info import USER_INFO
14 | from inside.Template.Meta_Singleton import SINGLETON
15 |
16 | __all__ = ['INFO_MANAGE']
17 |
18 |
19 | class INFO_MANAGE(metaclass=SINGLETON):
20 | """信息管理类"""
21 | __Get: GET_INFO
22 |
23 | def __init__(self):
24 | """
25 | INFO_MANAGE()
26 | 初始化
27 |
28 | """
29 | self.__self = '_'+type(self).__name__
30 |
31 | def Init(self, token: str) -> None:
32 | """
33 | Init(token: str) -> None
34 | 初始化信息获取
35 |
36 | :param token: 令牌
37 | :return: None
38 | """
39 | self.__Get = GET_INFO(token=token)
40 |
41 | @property
42 | def Get(self) -> GET_INFO:
43 | """
44 | Get -> GET_INFO
45 | 信息获取类
46 |
47 | :return: GET_INFO
48 | """
49 | if hasattr(self, self.__self+'__Get'):
50 | return self.__Get
51 | raise Exception(f"检测到更新信息类未初始化\n"
52 | f"请先调用{type(self).__name__}().Init(token=xxx)")
53 |
54 | @property
55 | def Id(self) -> int:
56 | """
57 | Id - > int
58 | 用户ID
59 |
60 | :return: int
61 | """
62 | return USER_INFO().User_Id
63 |
64 | @property
65 | def Aggregate_Score(self) -> float:
66 | """
67 | Aggregate_Score -> float
68 | 总积分
69 |
70 | :return: float
71 | """
72 | self.Get.Get_Aggregate_Score()
73 | return USER_INFO().Aggregate_Score
74 |
75 | @property
76 | def Daily_Score(self) -> float:
77 | """
78 | Daily_Score -> float
79 | 每日积分
80 |
81 | :return: float
82 | """
83 | self.Get.Get_Daily_Score()
84 | return USER_INFO().Daily_Score
85 |
86 | @property
87 | def Level_Info(self) -> Tuple[int, str, int]:
88 | """
89 | Level -> int
90 | 等级、段位、全国排名
91 |
92 | :return: Tuple[int, str, int]
93 | """
94 | self.Get.Get_Level()
95 | return (USER_INFO().Level,
96 | USER_INFO().Level_Name,
97 | USER_INFO().Rank_Accumulate_In_Country)
98 |
99 | @property
100 | def Task_Bar(self) -> Dict[int, TASK_INFO]:
101 | """
102 | Task_Bar -> Dict[int, TASK_INFO]
103 | 任务进度
104 |
105 | :return: Dict[int, TASK_INFO]
106 | """
107 | self.Get.Get_Task_Bar()
108 | return USER_INFO().Task_Bar
109 |
--------------------------------------------------------------------------------
/inside/Config/User_Agent.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : User_Agent.py
8 | # @Function : User_Agent大全
9 | from inside.Template.Meta_Singleton import SINGLETON
10 | from inside.Tools.Random import RANDOM
11 |
12 | __all__ = ['USER_AGENT']
13 |
14 |
15 | class USER_AGENT(metaclass=SINGLETON):
16 | """模拟浏览器头类"""
17 |
18 | __User_Agents = [
19 | 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0'
20 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60',
21 | 'Opera/8.0 (Windows NT 5.1; U; en)',
22 | 'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
23 | 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
24 | 'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
25 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2',
26 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
27 | 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
28 | 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
29 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
30 | 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
31 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11',
32 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
33 | 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0',
34 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36'
35 | 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36'
36 | ]
37 |
38 | def __init__(self):
39 | """
40 | USER_AGENT()
41 | 初始化,随机挑选一个头
42 |
43 | """
44 | ran = RANDOM()
45 | self.__user_agent = self.__User_Agents[
46 | ran.Int(a=0, b=len(self.__User_Agents)-1)
47 | ]
48 |
49 | @property
50 | def User_Agent(self) -> str:
51 | """
52 | User_Agent -> str
53 | 获取浏览器伪装,由于初始化时,已随机设定过,此后一直是同一条。
54 |
55 | :return: str
56 | """
57 | return self.__user_agent
58 |
--------------------------------------------------------------------------------
/inside/Template/ABC_System_Args.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : ABC_System_Args.py
8 | # @Function : 系统基类
9 | from abc import abstractmethod
10 |
11 | from inside.Template.Meta_Singleton import SINGLETON_ABC
12 |
13 | __all__ = ["SYSTEM_ARGS"]
14 |
15 |
16 | class ABC_SYSTEM_ARGS(metaclass=SINGLETON_ABC):
17 | """抽象系统类"""
18 |
19 | @abstractmethod
20 | def System(self) -> str:
21 | """
22 | System() -> str
23 | 操作系统
24 |
25 | :return: str
26 | """
27 |
28 | @abstractmethod
29 | def Clear(self) -> str:
30 | """
31 | Clear() -> str
32 | 清空控制台命令
33 |
34 | :return: str
35 | """
36 | pass
37 |
38 | @abstractmethod
39 | def Driver(self) -> str:
40 | """
41 | Driver() -> str
42 | 驱动文件名称
43 |
44 | :return: str
45 | """
46 | pass
47 |
48 | @abstractmethod
49 | def Driver_Chmod(self) -> str:
50 | """
51 | Driver_Chmod() -> str
52 | 添加驱动文件执行权限命令
53 |
54 | :return: str
55 | """
56 | pass
57 |
58 | @abstractmethod
59 | def Chrome(self) -> bool:
60 | """
61 | Chrome() -> bool
62 | 谷歌浏览器是否安装
63 |
64 | :return: bool
65 | """
66 | pass
67 |
68 | @abstractmethod
69 | def Chrome_Version(self) -> str:
70 | """
71 | Chrome_Version() -> str
72 | 谷歌浏览器版本号
73 |
74 | :return: str
75 | """
76 | pass
77 |
78 |
79 | class SYSTEM_ARGS(ABC_SYSTEM_ARGS):
80 | """系统实际继承类,使每个方法都变为属性"""
81 |
82 | @property
83 | def System(self) -> str:
84 | """
85 | System() -> str
86 | 操作系统
87 |
88 | :return: str
89 | """
90 | pass
91 |
92 | @property
93 | def Clear(self) -> str:
94 | """
95 | Clear -> str
96 | 清空控制台命令
97 |
98 | :return: str
99 | """
100 | pass
101 |
102 | @property
103 | def Driver(self) -> str:
104 | """
105 | Driver -> str
106 | 驱动文件名称
107 |
108 | :return: str
109 | """
110 | pass
111 |
112 | @property
113 | def Driver_Chmod(self) -> str:
114 | """
115 | Driver_Chmod -> str
116 | 添加驱动文件执行权限命令
117 |
118 | :return: str
119 | """
120 | pass
121 |
122 | @property
123 | def Chrome(self) -> bool:
124 | """
125 | Chrome -> bool
126 | 谷歌浏览器是否安装
127 |
128 | :return: bool
129 | """
130 | pass
131 |
132 | @property
133 | def Chrome_Version(self) -> str:
134 | """
135 | Chrome_Version -> str
136 | 谷歌浏览器版本号
137 |
138 | :return: str
139 | """
140 | pass
141 |
142 |
--------------------------------------------------------------------------------
/inside/DB/DB_Manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Manage.py
8 | # @Function : 数据库管理
9 | import sqlite3
10 | from inside.Config.Path import PATH
11 | from inside.DB.DB_Article import DB_ARTICLE
12 | from inside.DB.DB_Baidu_AI import DB_BAIDU_AI
13 | from inside.DB.DB_Check import DB_CHECK
14 | from inside.DB.DB_Project import DB_PROJECT
15 | from inside.DB.DB_Task import DB_TASK
16 | from inside.DB.DB_User import DB_USER
17 | from inside.DB.DB_Video import DB_VIDEO
18 | from inside.Template.Meta_Singleton import SINGLETON
19 |
20 | __all__ = ['DB_MANAGE']
21 |
22 |
23 | class DB_MANAGE(metaclass=SINGLETON):
24 | """数据库管理类"""
25 |
26 | def __init__(self):
27 | """
28 | DB_MANAGE()
29 | 初始化时自动检查数据库信息
30 |
31 | """
32 | DB_CHECK().Check_Dir()
33 | self.__connect = sqlite3.connect(PATH().DB_File)
34 | self.__cursor = self.__connect.cursor()
35 | self.__Check_Table()
36 |
37 | def __Check_Table(self) -> None:
38 | """
39 | __Check_Table() -> None
40 | 检查表
41 |
42 | :return: None
43 | """
44 | DB_CHECK().Check_Table(
45 | connect=self.__connect,
46 | cursor=self.__cursor
47 | )
48 |
49 | def Quit(self):
50 | self.__connect.close()
51 |
52 | @property
53 | def User(self) -> DB_USER:
54 | """
55 | User -> DB_USER
56 | 用户表操作对象
57 |
58 | :return: DB_USER
59 | """
60 | return DB_USER(connect=self.__connect, cursor=self.__cursor)
61 |
62 | @property
63 | def Task(self) -> DB_TASK:
64 | """
65 | Task -> DB_TASK
66 | 任务表操作对象
67 |
68 | :return: DB_TASK
69 | """
70 | return DB_TASK(connect=self.__connect, cursor=self.__cursor)
71 |
72 | @property
73 | def Article(self) -> DB_ARTICLE:
74 | """
75 | Article -> DB_ARTICLE
76 | 文章表操作对象
77 |
78 | :return: DB_ARTICLE
79 | """
80 | return DB_ARTICLE(connect=self.__connect, cursor=self.__cursor)
81 |
82 | @property
83 | def Video(self) -> DB_VIDEO:
84 | """
85 | Video -> DB_VIDEO
86 | 视频表操作对象
87 |
88 | :return: DB_VIDEO
89 | """
90 | return DB_VIDEO(connect=self.__connect, cursor=self.__cursor)
91 |
92 | @property
93 | def Project(self) -> DB_PROJECT:
94 | """
95 | Project -> DB_PROJECT
96 | 专项答题表操作对象
97 |
98 | :return: DB_PROJECT
99 | """
100 | return DB_PROJECT(connect=self.__connect, cursor=self.__cursor)
101 |
102 | @property
103 | def Baidu_AI(self) -> DB_BAIDU_AI:
104 | """
105 | Baidu_AI -> DB_BAIDU_AI
106 | 百度AI表操作对象
107 |
108 | @return: DB_BAIDU_AI
109 | """
110 | return DB_BAIDU_AI(connect=self.__connect, cursor=self.__cursor)
111 |
--------------------------------------------------------------------------------
/inside/Driver/Driver_Manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/16
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Driver_Manage.py
8 | # @Function : 驱动管理
9 | import os
10 |
11 | from selenium.webdriver.chrome.webdriver import WebDriver
12 |
13 | from inside.Config.Api import API
14 | from inside.Config.Path import PATH
15 | from inside.Config.System import SYSTEM
16 | from inside.Driver.Driver_Check import DRIVER_CHECK
17 | from inside.Driver.Driver_Download import DRIVER_DOWNLOAD
18 | from inside.Driver.Driver_Init import DRIVER_INIT
19 | from inside.Template.Meta_Singleton import SINGLETON
20 |
21 | __all__ = ['DRIVER_MANAGE']
22 |
23 |
24 | class DRIVER_MANAGE(metaclass=SINGLETON):
25 | """驱动管理类"""
26 |
27 | def __init__(self):
28 | """
29 | 初始化时会要求设置选项
30 | """
31 | self.__Driver_Check()
32 |
33 | @classmethod
34 | def __Check_Dir(cls) -> bool:
35 | """
36 | Check_Dir() -> None
37 | 检测驱动目录是否存在,如不存在则自动创建
38 |
39 | :return: bool
40 | """
41 | if not DRIVER_CHECK().Dir:
42 | print(f"检测到驱动目录未创建\n"
43 | f"自动创建中")
44 | os.mkdir(PATH().Driver)
45 | print(f"驱动目录为{PATH().Driver}")
46 | return False
47 | return True
48 |
49 | def __Driver_Check(self) -> None:
50 | """
51 | __Driver_Check() -> None
52 | 驱动检查,确保驱动能够正常使用
53 |
54 | :return: None
55 | """
56 | temp = False
57 | if not temp and not self.__Check_Dir():
58 | temp = True
59 | if not temp and not DRIVER_CHECK().File:
60 | print(f"检测到驱动未下载")
61 | temp = True
62 | if not temp and not DRIVER_CHECK().Driver_Chrome_Version(
63 | system=SYSTEM()):
64 | print(f"检测到驱动不支持本机Chrome")
65 | temp = True
66 | if temp:
67 | print(f"驱动自动下载中")
68 | size = DRIVER_DOWNLOAD().Download(
69 | link=API().Driver.Download(system=SYSTEM()))
70 | print(f"驱动自动下载完毕\n"
71 | f"文件大小为{size / 1024 / 1024}MB")
72 | if not DRIVER_CHECK().Execute_Permission:
73 | print(f"检测到驱动没有执行权限\n"
74 | f"自动添加执行权限")
75 | DRIVER_CHECK().Add_Execute_Permission(system=SYSTEM())
76 | print(f"添加执行权限完毕")
77 |
78 | @property
79 | def Task(self) -> WebDriver:
80 | """
81 | Task -> WebDriver
82 | 任务浏览器驱动器,关闭时,请务必使用Task_Quit
83 |
84 | :return: WebDriver
85 | """
86 | return DRIVER_INIT().Task_Driver
87 |
88 | @property
89 | def Task_Quit(self) -> str:
90 | """
91 | Task_Quit -> str
92 | 任务浏览器驱动器关闭
93 |
94 | :return: str
95 | """
96 | return DRIVER_INIT().Task_Quit
97 |
98 | @property
99 | def QR(self) -> WebDriver:
100 | """
101 | QR -> WebDriver
102 | 二维码浏览器驱动器,关闭时,请务必使用QR_Quit
103 |
104 | :return: WebDriver
105 | """
106 | return DRIVER_INIT().QR_Driver
107 |
108 | @property
109 | def QR_Quit(self) -> str:
110 | """
111 | QR_Quit -> str
112 | 二维码浏览器驱动器关闭
113 |
114 | :return: str
115 | Success: 退出成功
116 | Nonexistence: 不存在任务浏览器
117 | """
118 | return DRIVER_INIT().QR_Quit
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 项目简介
2 |
3 |
4 |
5 | > 学习强国自动化脚本,解放你的时间!
6 | >
7 | > 使用Selenium、requests、mitmpoxy、百度智能云文字识别开发而成
8 |
9 |
10 |
11 | # 使用说明
12 |
13 | > **注**:Chrome版本
14 | >
15 | > 
16 | >
17 | > 驱动会自动下载
18 | >
19 | > 首次使用会生成数据库文件db.db,用于提高文章、视频任务效率。
20 |
21 |
22 |
23 | ## 依赖安装
24 |
25 | > pip install -r requirements.txt
26 | >
27 | > 没有梯子的同学可使用国内阿里源:
28 | >
29 | > pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
30 |
31 |
32 |
33 | ## 使用方法
34 |
35 | > **一定要找个网络好的地方,不然可能会出现错误**
36 | >
37 | > 1、控制台运行:python main.py
38 | >
39 | > 2、选择选项(如非必要,尽量选择不显示自动化过程,以免误操)
40 | >
41 | > 等待片刻,连接学习强国服务器需要时间,等待时间与网速关系很大。
42 | >
43 | > 3、扫描二维码登录
44 | >
45 | > 4、选择任务(暂时只支持文章、视频、每日答题、每周答题、专项答题,后续功能正在开发,不过暂时也差不多够用了,45分呢),可多选,不过每个选项要用空格隔开,选择文章或视频时,等待时间稍久一点。
46 | >
47 | > 5、任务完成后需手动结束程序
48 |
49 |
50 |
51 | ## 使用示例
52 |
53 | > 
54 | >
55 | > 
56 | >
57 | > 
58 | >
59 | > 
60 | >
61 | > 
62 | >
63 | > 
64 | >
65 | > 
66 | >
67 | > 
68 | >
69 | > 
70 |
71 |
72 |
73 | ## 百度智能云操作流程
74 |
75 | > 1、登录控制台
76 | >
77 | > 点击→[百度智能云](https://login.bce.baidu.com/?account=&redirect=http://console.bce.baidu.com/ai/#/ai/ocr/overview/index, "百度智能云")
78 | >
79 | > 
80 | >
81 | > 2、创建应用
82 | >
83 | > 
84 | >
85 | > 3、选择选项
86 | >
87 | > 
88 | >
89 | > 
90 | >
91 | > 4、获取API Key、Secret Key
92 | >
93 | > 
94 |
95 |
96 |
97 |
98 |
99 | ## 版本说明
100 |
101 | > - [ ] ~~v0.1:文章、视频,分数:25~~
102 | > - [ ] ~~v0.2:优化文章、优化视频、每日答题(百分百正确),分数30~~
103 | > - [ ] ~~v0.3:新增每周答题、专项答题(也是百分百正确),分数45~~
104 | > - [ ] ~~v0.31:优化记录存储、优化目录结构、优化配置文件结构,增加进度条、增加自动下载驱动、增加系统兼容(Linux、Windows、MacOS)~~
105 | > - [ ] ~~v1.0: 重构整个项目,增加持久化、驱动自动检测与谷歌浏览器匹配、驱动自主下载、更快的登录、文章和视频自适应、更快更精准的答题、加强的防检测、每个文件都有说明注释(便于各位大佬修改)~~
106 | > - [x] v1.1: 由于专项答题视频答案匹配问题,现加入百度智能云的文字识别功能,可将视频中的答案提取出来,不过答案还需手动填写,因为提取的答案暂时没有好的办法过滤。至少不用看视频了是不2333.
107 |
108 |
109 |
110 | ## 附语
111 |
112 | > 在持久化登录方面思考了很久,要不要做批量持久化?,后来想了想,本项目的目的是为了帮助没有时间做学习强国任务的个人节省时间,如果做了批量的话,恐怕会沦为某些人的牟利工具。所以最后决定只做单用户持久模式,如果有想做批量的话,建议细读学习强国App积分页的提醒警示。
113 | >
114 | > 希望大佬点点start,给点动力,希望能给大家带来更多的功能!
115 |
116 |
--------------------------------------------------------------------------------
/inside/Baidu_AI/Baidu_AI_Tools.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/5
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Baidu_AI_Tools.py
8 | # @Function : 百度AI图像文字识别工具
9 | import requests
10 | import base64
11 | import cv2
12 | from PIL import Image
13 |
14 | from inside.DB.Table_Class.Baidu_AI import BAIDU_AI
15 | from inside.Template.Meta_Singleton import SINGLETON
16 | __all__ = ['BAIDU_AI_TOOLS']
17 |
18 |
19 | class BAIDU_AI_TOOLS(metaclass=SINGLETON):
20 | """百度AI工具"""
21 | __token: str
22 |
23 | def __init__(self, baidu_ai: BAIDU_AI):
24 | """
25 | BAIDU_AI_TOOLS(baidu_ai: BAIDU_AI)
26 | 初始化
27 |
28 | Args:
29 | baidu_ai: 百度AI
30 | """
31 | self.__self = '_'+type(self).__name__
32 | self.__ai = baidu_ai
33 | self.__Set_Token()
34 |
35 | def __Set_Token(self) -> None:
36 | """
37 | __Set_Token() -> None
38 | 获取设置token
39 |
40 | Returns: None
41 |
42 | """
43 | host = f'https://aip.baidubce.com/oauth/2.0/token?grant_type' \
44 | f'=client_credentials&client_id={self.__ai.Ak}&client_' \
45 | f'secret={self.__ai.Sk}'
46 | html = requests.get(host)
47 | self.__token = html.json().get('access_token')
48 |
49 | @classmethod
50 | def Cut(cls, video_link: str) -> bytes:
51 | """
52 | Cut(video_link: str) -> bytes
53 | 取帧迭代器
54 |
55 | Args:
56 | video_link: 视频链接
57 |
58 | Returns: 图片
59 |
60 | """
61 | video = cv2.VideoCapture(video_link)
62 | if video.isOpened():
63 | frame_rate = video.get(5)
64 | frame_number = video.get(7)
65 | interval = frame_number // frame_rate
66 | video.set(cv2.CAP_PROP_POS_FRAMES, frame_rate * (interval - 2))
67 | num = 0
68 | rval, frame = video.read()
69 | while rval:
70 | if num % frame_rate == 0:
71 | ret, buf = cv2.imencode('.jpg', frame)
72 | pic = Image.fromarray(buf).tobytes()
73 | yield pic
74 | rval, frame = video.read()
75 | num += 1
76 |
77 | def Answer(self, video_link: str) -> str:
78 | """
79 | Answer(video_link: str) -> str
80 | 获取答案
81 | 通用文字识别(高精度版)
82 |
83 | Args:
84 | video_link: 视频链接
85 |
86 | Returns: str
87 |
88 | """
89 | video = self.Cut(video_link=video_link)
90 | while True:
91 | try:
92 | img = base64.b64encode(next(video))
93 | except StopIteration:
94 | break
95 | request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1" \
96 | "/accurate_basic"
97 | params = {"image": img}
98 | request_url = request_url + "?access_token=" + self.__token
99 | headers = {'content-type': 'application/x-www-form-urlencoded'}
100 | response = requests.post(request_url, data=params, headers=headers)
101 | if response:
102 | for words in response.json()['words_result']:
103 | for topic in ('考题答案:', '考题答案', '参考答案:', '参考答案'):
104 | if topic in words['words']:
105 | return words['words'].replace(topic, '').strip()
106 |
--------------------------------------------------------------------------------
/inside/DB/DB_Baidu_AI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Baidu_AI.py
8 | # @Function : 百度AI操作类
9 | from sqlite3 import Connection, Cursor
10 |
11 | from inside.DB.DB_Config import DB_CONFIG
12 | from inside.DB.Table_Class.Baidu_AI import BAIDU_AI
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['DB_BAIDU_AI']
16 |
17 |
18 | class DB_BAIDU_AI(metaclass=SINGLETON):
19 | """百度AI表操作类"""
20 |
21 | def __init__(self, connect: Connection, cursor: Cursor):
22 | """
23 | 初始化
24 |
25 | :param connect: 数据库连接对象
26 | :param cursor: 数据库光标
27 | """
28 | self.__connect = connect
29 | self.__cursor = cursor
30 | self.__name = DB_CONFIG().Baidu_AI
31 | self.__fields = DB_CONFIG().Baidu_AI_Fields
32 |
33 | def Insert(self, baidu_ai: BAIDU_AI) -> None:
34 | """
35 | Insert(article: ARTICLE) -> None
36 | 插入百度AI,如表不为空则清空表
37 |
38 | @param baidu_ai: 百度AI
39 | @return: None
40 | """
41 | if not self.Empty():
42 | self.Delete()
43 | sql = f'''INSERT INTO
44 | {self.__name} (
45 | {self.__fields[1]},
46 | {self.__fields[2]})
47 | VALUES (?, ?);'''
48 | data = (
49 | baidu_ai.Ak,
50 | baidu_ai.Sk
51 | )
52 | self.__cursor.execute(sql, data)
53 | self.__connect.commit()
54 |
55 | def Seq_Init(self) -> None:
56 | """
57 | Seq_Init() -> None
58 | 将百度AI表的数据序号重置为0
59 |
60 | @return: None
61 | """
62 | sql = f'''UPDATE sqlite_sequence SET seq=0 WHERE name='{self.__name}';'''
63 | self.__cursor.execute(sql)
64 | self.__connect.commit()
65 |
66 | def Delete(self) -> None:
67 | """
68 | Delete() -> None
69 | 清空百度AI表,并重置序号
70 |
71 | @return: None
72 | """
73 | sql = f'''DELETE FROM {self.__name};'''
74 | self.__cursor.execute(sql)
75 | self.__connect.commit()
76 | self.Seq_Init()
77 |
78 | def Query(self) -> BAIDU_AI:
79 | """
80 | Query() -> BAIDU_AI
81 | 获取百度AI
82 |
83 | @return: BAIDU_AI
84 | """
85 | sql = f'''SELECT * FROM {self.__name} LIMIT 1;'''
86 | result = self.__cursor.execute(sql)
87 | temp = next(result)
88 | return BAIDU_AI(ak=temp[1], sk=temp[2])
89 |
90 | def Empty(self) -> bool:
91 | """
92 | Empty() -> bool
93 | 是否为空
94 |
95 | @return: bool
96 | """
97 | sql = f'''SELECT 1 FROM {self.__name} LIMIT 1;'''
98 | result = self.__cursor.execute(sql)
99 | try:
100 | next(result)
101 | return False
102 | except StopIteration:
103 | return True
104 |
105 | def Exist(self, baidu_ai: BAIDU_AI) -> bool:
106 | """
107 | Exist(baidu_ai: BAIDU_AI = None) -> bool
108 | 是否存在指定百度AI
109 |
110 | @param baidu_ai: 百度AI
111 | @return:
112 | """
113 | sql = f'''SELECT 1 FROM {self.__name}
114 | WHERE {self.__fields[1]}=?
115 | AND {self.__fields[2]}=?;'''
116 | data = (
117 | baidu_ai.Ak,
118 | baidu_ai.Sk
119 | )
120 | result = self.__cursor.execute(sql, data)
121 | try:
122 | next(result)
123 | return True
124 | except StopIteration:
125 | return False
126 |
--------------------------------------------------------------------------------
/inside/Config/System.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : System.py
8 | # @Function : 系统适配
9 | import math
10 | import sys
11 | from typing import FrozenSet
12 |
13 | from inside.Config.SystemArgs import WINDOWS, LINUX, MACOS
14 | from inside.Template.ABC_System_Args import SYSTEM_ARGS
15 |
16 | __all__ = ['SYSTEM']
17 |
18 |
19 | class SYSTEM(SYSTEM_ARGS):
20 | """系统类"""
21 | __supports = frozenset({'linux', 'win32', 'darwin'})
22 | __supports_map = {
23 | 'linux': 'Linux',
24 | 'win32': 'Windows',
25 | 'darwin': 'MacOs'
26 | }
27 |
28 | # 支持系统
29 | @property
30 | def Supports(self) -> FrozenSet:
31 | """
32 | Supports -> FrozenSet
33 | 支持系统集合
34 |
35 | :return: FrozenSet
36 | """
37 | return self.__supports
38 |
39 | # 系统
40 | @property
41 | def Name(self) -> str:
42 | """
43 | Name -> str
44 | 系统名称
45 |
46 | :return: str
47 | """
48 | return sys.platform
49 |
50 | # 位数
51 | @property
52 | def Bit(self) -> int:
53 | """
54 | Bit -> int
55 | 系统位数
56 |
57 | :return: int
58 | """
59 | return int(math.log2(sys.maxsize + 1) + 1)
60 |
61 | # 系统参数
62 | @property
63 | def __System_Args(self) -> SYSTEM_ARGS:
64 | """
65 | __System_Args -> SYSTEM_ARGS
66 | 获取当前操作系统类
67 |
68 | :return: SYSTEM_ARGS
69 | """
70 | if self.Name not in self.Supports:
71 | print(f"检测到运行环境为{self.Name}操作系统\n"
72 | f"本程序暂不支持此系统\n"
73 | f"仅支持{tuple(self.__supports_map.values())}")
74 | exit(code=0)
75 | else:
76 | if self.Name == 'win32':
77 | return WINDOWS()
78 | elif self.Name == 'linux':
79 | return LINUX()
80 | elif self.Name == 'darwin':
81 | return MACOS()
82 |
83 | @property
84 | def System(self) -> str:
85 | """
86 | System -> str
87 | 操作系统
88 |
89 | :return: str
90 | """
91 | return self.__System_Args.System
92 |
93 | @property
94 | def Clear(self) -> str:
95 | """
96 | Clear -> str
97 | 清空控制台命令
98 |
99 | :return: str
100 | """
101 | return self.__System_Args.Clear
102 |
103 | @property
104 | def Driver(self) -> str:
105 | """
106 | Driver -> str
107 | 驱动文件名称
108 |
109 | :return: str
110 | """
111 | return self.__System_Args.Driver
112 |
113 | @property
114 | def Driver_Chmod(self) -> str:
115 | """
116 | Driver_Chmod -> str
117 | 添加驱动文件执行权限命令
118 |
119 | :return:
120 | """
121 | return self.__System_Args.Driver_Chmod
122 |
123 | @property
124 | def Chrome(self) -> bool:
125 | """
126 | Chrome -> bool
127 | 谷歌浏览器是否安装
128 |
129 | :return: bool
130 | """
131 | return self.__System_Args.Chrome
132 |
133 | @property
134 | def Chrome_Version(self) -> str:
135 | """
136 | Chrome_Version -> str
137 | 谷歌浏览器版本号
138 |
139 | :return: str
140 | """
141 | return self.__System_Args.Chrome_Version
142 |
143 | def Check_Chrome(self) -> None:
144 | """
145 | Check_Chrome() -> None
146 | 检测谷歌浏览器是否安装,未安装则退出程序
147 |
148 | :return: None
149 | """
150 | if not self.Chrome:
151 | print("检测到谷歌浏览器未安装,无法进行本程序")
152 | exit(code=0)
153 |
--------------------------------------------------------------------------------
/inside/Options/Options_Manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/23
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Options_Manage.py
8 | # @Function : 选项管理
9 | from inside.Config.Path import PATH
10 | from inside.Baidu_AI.Baidu_AI_Manage import BAIDU_AI_MANAGE
11 | from inside.Options.Options import OPTIONS
12 |
13 | from inside.Template.Meta_Singleton import SINGLETON
14 | from inside.Tools import Quit
15 |
16 | __all__ = ['OPTIONS_MANAGE']
17 |
18 |
19 | class OPTIONS_MANAGE(metaclass=SINGLETON):
20 | """选项管理类"""
21 |
22 | @classmethod
23 | def __Auto(cls) -> None:
24 | """
25 | __Auto() -> None
26 | 禁音选项(默认: True)
27 |
28 | :return: None
29 | """
30 | auto = input('是否静音(Y/n):').strip()
31 | if auto in ['y', 'Y', '']:
32 | OPTIONS().Mute_Audio = True
33 | else:
34 | OPTIONS().Mute_Audio = False
35 |
36 | @classmethod
37 | def __Headless(cls) -> None:
38 | """
39 | __Headless() -> None
40 | 显示过程选项(默认: False)
41 |
42 | :return: None
43 | """
44 | headless = input('是否显示自动化过程(y/N):').strip()
45 | if headless in ['y', 'Y']:
46 | OPTIONS().Headless = False
47 | else:
48 | OPTIONS().Headless = True
49 |
50 | @classmethod
51 | def __Token(cls) -> None:
52 | """
53 | __Token() -> None
54 | 持久化选项(默认:True)
55 |
56 | :return: None
57 | """
58 | token = input('是否持久化登录(Y/n):').strip()
59 | if token in ['y', 'Y', '']:
60 | OPTIONS().Token = True
61 | else:
62 | OPTIONS().Token = False
63 |
64 | @classmethod
65 | def __Baidu_AI(cls) -> None:
66 | """
67 | __Baidu_AI() -> None
68 | 百度AI选项(默认:False)
69 |
70 | Returns: None
71 |
72 | """
73 | baidu_ai = input('是否使用百度AI(y/N):').strip()
74 | if baidu_ai in ['y', 'Y']:
75 | OPTIONS().Baidu_AI = True
76 | BAIDU_AI_MANAGE.Verify()
77 | with open(PATH().Baidu_AI_On, 'w', encoding='utf-8') as f:
78 | f.write("1")
79 | f.close()
80 | else:
81 | OPTIONS().Baidu_AI = False
82 | with open(PATH().Baidu_AI_On, 'w', encoding='utf-8') as f:
83 | f.write("0")
84 | f.close()
85 |
86 | @classmethod
87 | def Init_Options(cls) -> None:
88 | """
89 | Options() -> None
90 | 选项初始化
91 |
92 | :return: None
93 | """
94 | cls.__Auto()
95 | cls.__Headless()
96 | cls.__Token()
97 | cls.__Baidu_AI()
98 |
99 | @classmethod
100 | def Task_Options(cls, hint: str = None) -> None:
101 | """
102 | Task_Options() -> None
103 | 任务选项初始化
104 |
105 | :return: None
106 | """
107 | OPTIONS().Task_Option_Set_Off_All()
108 | print("可选任务:")
109 | print("0、全选\t", end='')
110 | for key, value in OPTIONS().Task_Options.items():
111 | print(f"{key}、{value}\t", end='')
112 | print(hint if hint else '')
113 | options = input("选择任务(空为退出):").strip()
114 | if not options:
115 | Quit.Quit()
116 | exit(code=0)
117 | try:
118 | options = set([int(x) for x in options.split()])
119 | if 0 in options:
120 | OPTIONS().Task_Option_Set_On_All()
121 | return None
122 | if options - set(OPTIONS().Task_Options.keys()):
123 | cls.Task_Options(hint="\n请输入规定的选项序号")
124 | for seq in options:
125 | OPTIONS().Task_Option_Set_On(seq=seq)
126 | except ValueError:
127 | cls.Task_Options(hint="\n请输入数字")
128 |
129 |
130 | _inst = OPTIONS_MANAGE
131 | Init_Options = _inst.Init_Options
132 |
--------------------------------------------------------------------------------
/inside/DB/DB_Task.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Task.py
8 | # @Function : 任务表操作
9 | from sqlite3 import Connection, Cursor
10 |
11 | from inside.DB.DB_Config import DB_CONFIG
12 | from inside.DB.Table_Class.Task import TASK
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['DB_TASK']
16 |
17 |
18 | class DB_TASK(metaclass=SINGLETON):
19 | """任务表操作类"""
20 |
21 | def __init__(self, connect: Connection, cursor: Cursor):
22 | """
23 | 初始化
24 |
25 | :param connect: 数据库连接对象
26 | :param cursor: 光标
27 | """
28 | self.__connect = connect
29 | self.__cursor = cursor
30 | self.__name = DB_CONFIG().Task
31 | self.__fields = DB_CONFIG().Task_Fields
32 |
33 | def Insert(self, task: TASK) -> None:
34 | """
35 | Insert(task: TASK) -> None
36 | 插入任务, 如任务已存在则不进行操作
37 |
38 | :param task: 任务
39 | :return: None
40 | """
41 | if not self.Exist(task=task):
42 | sql = f'''INSERT INTO
43 | {self.__name} (
44 | {self.__fields[1]},
45 | {self.__fields[2]})
46 | VALUES (?, ?);'''
47 | data = (
48 | task.Link,
49 | task.Is_Read_DB
50 | )
51 | self.__cursor.execute(sql, data)
52 | self.__connect.commit()
53 |
54 | def Update(self, task: TASK) -> None:
55 | """
56 | Update(task: TASK) -> None
57 | 更新任务为已读, 如任务不存在则不进行操作
58 |
59 | :param task: 任务
60 | :return: None
61 | """
62 | if self.Exist(task=task):
63 | sql = f'''UPDATE {self.__name}
64 | SET {self.__fields[2]}=1
65 | WHERE {self.__fields[1]}=?'''
66 | data = (task.Link, )
67 | self.__cursor.execute(sql, data)
68 | self.__connect.commit()
69 |
70 | def Query(self) -> TASK:
71 | """
72 | Query() -> TASK
73 | 随机获取一条未读任务
74 |
75 | :return: TASK
76 | """
77 | sql = f'''SELECT * FROM {self.__name}
78 | WHERE {self.__fields[2]}=0 ORDER BY RANDOM() LIMIT 1;'''
79 | result = self.__cursor.execute(sql)
80 | temp = next(result)
81 | task = TASK(
82 | link=temp[1],
83 | isread=temp[2]
84 | )
85 | return task
86 |
87 | def Exist_Enough(self) -> bool:
88 | """
89 | Exist_Enough() -> bool
90 | 是否存在未读任务
91 |
92 | :return: bool
93 | """
94 | sql = f'''SELECT 1 FROM {self.__name}
95 | WHERE {self.__fields[2]}=0 LIMIT 1;'''
96 | result = self.__cursor.execute(sql)
97 | try:
98 | next(result)
99 | return True
100 | except StopIteration:
101 | return False
102 |
103 | def Empty(self) -> bool:
104 | """
105 | Empty() -> bool
106 | 任务表是否为空
107 |
108 | :return: bool
109 | """
110 | sql = f'''SELECT 1 FROM {self.__name} LIMIT 1;'''
111 | result = self.__cursor.execute(sql)
112 | try:
113 | next(result)
114 | return False
115 | except StopIteration:
116 | return True
117 |
118 | def Exist(self, task: TASK) -> bool:
119 | """
120 | Exist(task: TASK) -> bool
121 | 是否存在指定的任务
122 |
123 | :param task: 任务
124 | :return: bool
125 | """
126 | sql = f'''SELECT 1 FROM {self.__name}
127 | WHERE {self.__fields[1]} =?;'''
128 | data = (task.Link, )
129 | result = self.__cursor.execute(sql, data)
130 | try:
131 | next(result)
132 | return True
133 | except StopIteration:
134 | return False
135 |
--------------------------------------------------------------------------------
/inside/DB/DB_User.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_User.py
8 | # @Function : 用户表操作
9 | from sqlite3 import Connection, Cursor
10 |
11 | from inside.DB.DB_Config import DB_CONFIG
12 | from inside.DB.Table_Class.User import USER
13 | from inside.Template.Meta_Singleton import SINGLETON
14 |
15 | __all__ = ['DB_USER']
16 |
17 |
18 | class DB_USER(metaclass=SINGLETON):
19 | """用户表操作类"""
20 |
21 | def __init__(self, connect: Connection, cursor: Cursor):
22 | """
23 | 初始化
24 |
25 | :param connect: 数据库连接对象
26 | :param cursor: 数据库光标
27 | """
28 | self.__connect = connect
29 | self.__cursor = cursor
30 | self.__name = DB_CONFIG().User
31 | self.__fields = DB_CONFIG().User_Fields
32 |
33 | def Insert(self, user: USER) -> None:
34 | """
35 | Insert(user: USER) -> None
36 | 插入用户,插入之前如果存在用户则清空用户表,以保证只有一个用户使用
37 |
38 | :param user: 用户
39 | :return: None
40 | """
41 | if self.Exist():
42 | self.Delete()
43 | sql = f'''INSERT INTO
44 | {self.__name}
45 | ({self.__fields[1]},
46 | {self.__fields[2]},
47 | {self.__fields[3]})
48 | VALUES(?, ?, ?);'''
49 | data = (
50 | user.Id,
51 | user.Token,
52 | user.Time
53 | )
54 | self.__cursor.execute(sql, data)
55 | self.__connect.commit()
56 |
57 | def Seq_Init(self) -> None:
58 | """
59 | Seq_Init() -> None
60 | 将用户表的数据序号重置为0
61 |
62 | :return: None
63 | """
64 | sql = f'''UPDATE sqlite_sequence SET seq=0 WHERE name='{self.__name}';'''
65 | self.__cursor.execute(sql)
66 | self.__connect.commit()
67 |
68 | def Delete(self) -> None:
69 | """
70 | Delete() -> None
71 | 清空用户表,并重置序号
72 |
73 | :return: None
74 | """
75 | sql = f'''DELETE FROM {self.__name};'''
76 | self.__cursor.execute(sql)
77 | self.__connect.commit()
78 | self.Seq_Init()
79 |
80 | def Update(self, user: USER) -> None:
81 | """
82 | Update(user: USER) -> None
83 | 更新指定用户token
84 |
85 | :param user: 用户
86 | :return: None
87 | """
88 | sql = f'''UPDATE {self.__name}
89 | SET {self.__fields[2]}=?
90 | WHERE {self.__fields[1]} =?;'''
91 | data = (
92 | user.Token,
93 | user.Id
94 | )
95 | self.__cursor.execute(sql, data)
96 | self.__connect.commit()
97 |
98 | def Query(self) -> USER:
99 | """
100 | Query() -> USER
101 | 获取一条用户
102 |
103 | :return: USER
104 | """
105 | sql = f'''SELECT * FROM {self.__name} LIMIT 1;'''
106 | result = self.__cursor.execute(sql)
107 | temp = next(result)
108 | user = USER(
109 | user_id=temp[1],
110 | token=temp[2]
111 | )
112 | return user
113 |
114 | def Exist(self) -> bool:
115 | """
116 | Exist() -> bool
117 | 是否存在用户
118 |
119 | :return: bool
120 | """
121 | sql = f'''SELECT 1 FROM {self.__name} LIMIT 1;'''
122 | result = self.__cursor.execute(sql)
123 | try:
124 | next(result)
125 | return True
126 | except StopIteration:
127 | return False
128 |
129 | def Exist_User(self, user: USER) -> bool:
130 | """
131 | Exist(user: USER) -> bool
132 | 是否存在指定用户
133 |
134 | :return: bool
135 | """
136 | sql = f'''SELECT 1 FROM {self.__name}
137 | WHERE {self.__fields[1]}=? LIMIT 1;'''
138 | data = (user.Id, )
139 | result = self.__cursor.execute(sql, data)
140 | try:
141 | next(result)
142 | return True
143 | except StopIteration:
144 | return False
145 |
--------------------------------------------------------------------------------
/inside/DB/DB_Video.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Video.py
8 | # @Function : 视频表操作
9 | from sqlite3 import Connection, Cursor
10 | from typing import List
11 |
12 | from inside.DB.DB_Config import DB_CONFIG
13 | from inside.DB.Table_Class.Video import VIDEO
14 | from inside.Template.Meta_Singleton import SINGLETON
15 |
16 | __all__ = ['DB_VIDEO']
17 |
18 |
19 | class DB_VIDEO(metaclass=SINGLETON):
20 | """视频表操作类"""
21 |
22 | def __init__(self, connect: Connection, cursor: Cursor):
23 | """
24 | 初始化
25 |
26 | :param connect: 数据库连接对象
27 | :param cursor: 数据库光标
28 | """
29 | self.__connect = connect
30 | self.__cursor = cursor
31 | self.__name = DB_CONFIG().Video
32 | self.__fields = DB_CONFIG().Video_Fields
33 |
34 | def Insert(self, video: VIDEO) -> None:
35 | """
36 | Insert(video: VIDEO) -> None
37 | 插入视频,如视频已存在则不进行操作
38 |
39 | :param video: 视频
40 | :return: None
41 | """
42 | if not self.Exist(video=video):
43 | sql = f'''INSERT INTO
44 | {self.__name}
45 | ({self.__fields[1]},
46 | {self.__fields[2]},
47 | {self.__fields[3]})
48 | VALUES (?, ?, ?);'''
49 | data = (
50 | video.Item,
51 | video.Link,
52 | video.Is_Read_DB
53 | )
54 | self.__cursor.execute(sql, data)
55 | self.__connect.commit()
56 |
57 | def Update(self, video: VIDEO) -> None:
58 | """
59 | Update(video: VIDEO) -> None
60 | 更新视频为已读,如视频不存在则不进行操作
61 |
62 | :param video: 视频
63 | :return: None
64 | """
65 | if self.Exist(video=video):
66 | sql = f'''UPDATE {self.__name}
67 | SET {self.__fields[3]}=1
68 | WHERE {self.__fields[1]}=?
69 | AND {self.__fields[2]}=?;'''
70 | data = (
71 | video.Item,
72 | video.Link
73 | )
74 | self.__cursor.execute(sql, data)
75 | self.__connect.commit()
76 |
77 | def Update_All(self) -> None:
78 | """
79 | Update_All() -> None
80 | 更新所有视频为未读
81 |
82 | :return: None
83 | """
84 | sql = f'''UPDATE {self.__name}
85 | SET {self.__fields[3]}=0;'''
86 | self.__cursor.execute(sql)
87 | self.__connect.commit()
88 |
89 | def Query(self, limit: int) -> List[VIDEO]:
90 | """
91 | Query(limit: int) -> List[VIDEO]
92 | 随机获取指定数量的未读视频
93 |
94 | :param limit: 数量
95 | :return: List[VIDEO]
96 | """
97 | sql = f'''SELECT * FROM {self.__name}
98 | WHERE {self.__fields[3]}=0 ORDER BY RANDOM() LIMIT ?;'''
99 | data = (limit, )
100 | result = self.__cursor.execute(sql, data)
101 | temp = []
102 | for value in result:
103 | video = VIDEO(
104 | item=value[1],
105 | link=value[2],
106 | isread=value[3]
107 | )
108 | temp.append(video)
109 | return temp
110 |
111 | def Exist_Enough(self, limit: int) -> bool:
112 | """
113 | Exist_Enough(limit: int) -> bool
114 | 是否存在指定数量的未读视频
115 |
116 | :param limit: 数量
117 | :return: bool
118 | """
119 | sql = f'''SELECT count() FROM {self.__name}
120 | WHERE {self.__fields[3]}=0 LIMIT ?;'''
121 | data = (limit, )
122 | result = self.__cursor.execute(sql, data)
123 | temp = next(result)
124 | return temp[0] >= limit
125 |
126 | def Exist(self, video: VIDEO) -> bool:
127 | """
128 | Exist(video: VIDEO) -> bool
129 | 是否存在指定的视频
130 |
131 | :param video: 视频
132 | :return: bool
133 | """
134 | sql = f'''SELECT 1 FROM {self.__name}
135 | WHERE {self.__fields[1]}=?
136 | AND {self.__fields[2]}=?;'''
137 | data = (
138 | video.Item,
139 | video.Link
140 | )
141 | result = self.__cursor.execute(sql, data)
142 | try:
143 | next(result)
144 | return True
145 | except StopIteration:
146 | return False
147 |
--------------------------------------------------------------------------------
/inside/Tools/Network.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/20
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Network.py
8 | # @Function : 网络日志
9 | import json
10 | import psutil
11 | import gc
12 | from _queue import Empty
13 | from queue import Queue
14 | from threading import Thread
15 | from typing import Dict
16 |
17 | from selenium.webdriver.chrome.webdriver import WebDriver
18 |
19 | __all__ = ['NETWORK']
20 |
21 | from inside.Template.Meta_Singleton import SINGLETON
22 |
23 |
24 | class NETWORK(metaclass=SINGLETON):
25 | """network日志类,实现原理为队列+线程"""
26 |
27 | __task: Thread
28 | __driver: WebDriver
29 |
30 | def __init__(self):
31 | """
32 | NETWORK()
33 | 初始化,队列
34 |
35 | """
36 | self.__self = '_'+type(self).__name__
37 | self.__queue = Queue(maxsize=0)
38 | self.__on = True
39 |
40 | def Init(self, driver: WebDriver):
41 | if not hasattr(self, self.__self+'__driver'):
42 | self.__driver = driver
43 | self.__task = Thread(target=self.__Get_Log)
44 | self.__task.start()
45 | else:
46 | print('已初始化')
47 |
48 | def __Get_Log(self) -> None:
49 | """
50 | __Get_Log() -> None
51 | 获取有效日志,并加入队列,结束条件为开关
52 |
53 | :return: None
54 | """
55 | while True:
56 | if not self.__on:
57 | break
58 | logs = self.__driver.get_log(log_type='performance')
59 | for log in logs:
60 | log['message'] = json.loads(log['message'])
61 | try:
62 | url = log['message']['message']['params']['request']['url']
63 | requestId = log['message']['message']['params']['requestId']
64 | while psutil.virtual_memory().used / psutil.virtual_memory().total >= 0.95:
65 | gc.collect()
66 | continue
67 | self.__queue.put({url: requestId})
68 | except KeyError:
69 | continue
70 |
71 | def __On_Logs(self) -> None:
72 | """
73 | __On_Logs() -> None
74 | 开启日志
75 | :return: None
76 | """
77 | if not self.__task.is_alive():
78 | self.__task = Thread(target=self.__Get_Log)
79 | self.__task.start()
80 |
81 | def Get(self) -> Dict:
82 | """
83 | Get() -> Dict
84 | 获取日志
85 |
86 | :return:Dict
87 | 格式为{url: requestId}
88 | """
89 | try:
90 | return self.__queue.get_nowait()
91 | except Empty:
92 | return {}
93 |
94 | def GetResponseBody(self, requestId: str) -> Dict:
95 | """
96 | GetResponseBody(requestId: str) -> Dict
97 | 获取指定请求Id的响应信息,考虑到网络延迟,可能会触发WebDriverException错误,
98 | 建议使用try/except对同一requestId进行循环获取
99 |
100 | :param requestId: str
101 | :return: Dict
102 | 格式为:{'base64Encoded': bool, 'body': str}
103 | """
104 | body = self.__driver.execute_cdp_cmd(
105 | cmd='Network.getResponseBody',
106 | cmd_args={'requestId': requestId}
107 | )
108 | return body
109 |
110 | def Clear(self) -> None:
111 | """
112 | Clear() -> None
113 | 清空缓存日志
114 |
115 | :return: None
116 | """
117 | self.__queue.queue.clear()
118 |
119 | @property
120 | def On(self) -> bool:
121 | """
122 | On -> bool
123 | 查看开关
124 |
125 | :return: bool
126 | """
127 | return self.__on
128 |
129 | @On.setter
130 | def On(self, on: bool) -> None:
131 | """
132 | On -> bool
133 | 设置开关
134 |
135 | :param on: bool
136 | :return: None
137 | """
138 | self.__on = on
139 | if self.__on:
140 | self.__On_Logs()
141 |
142 | @property
143 | def Is_Alive(self) -> bool:
144 | """
145 | Is_Alive -> bool
146 | 线程是否存活
147 |
148 | :return: bool
149 | """
150 | if hasattr(self, self.__self+'__task'):
151 | return self.__task.is_alive()
152 | return False
153 |
154 | def Quit(self) -> None:
155 | """
156 | Quit() -> None
157 | 退出
158 |
159 | :return: None
160 | """
161 | self.__on = False
162 |
--------------------------------------------------------------------------------
/inside/DB/DB_Config.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Config.py
8 | # @Function : 数据库配置文件(此配置文件不可更改)
9 | from inside.Template.Meta_Singleton import SINGLETON
10 |
11 | __all__ = ['DB_CONFIG']
12 |
13 |
14 | class DB_CONFIG(metaclass=SINGLETON):
15 | """
16 | 数据库配置类
17 | """
18 |
19 | @property
20 | def User(self) -> str:
21 | """
22 | User -> str
23 | 用户表名
24 |
25 | :return: str
26 | """
27 | return 'user'
28 |
29 | @property
30 | def User_Fields(self) -> tuple:
31 | """
32 | User_Fields -> tuple
33 | 用户表字段
34 |
35 | :return: tuple
36 | """
37 | return 'ID', 'USERID', 'TOKEN', 'TIME'
38 |
39 | @property
40 | def User_Fields_Types(self) -> tuple:
41 | """
42 | User_Fields_Types -> tuple
43 | 用户表字段类型
44 |
45 | :return: tuple
46 | """
47 | return 'INTEGER', 'INTEGER', 'TEXT', 'TEXT'
48 |
49 | @property
50 | def Task(self) -> str:
51 | """
52 | Task -> str
53 | 任务表名
54 |
55 | :return: str
56 | """
57 | return 'task'
58 |
59 | @property
60 | def Task_Fields(self) -> tuple:
61 | """
62 | Task_Fields -> tuple
63 | 任务表字段
64 |
65 | :return: tuple
66 | """
67 | return 'ID', 'LINK', 'ISREAD'
68 |
69 | @property
70 | def Task_Fields_Types(self) -> tuple:
71 | """
72 | Task_Fields_Types -> tuple
73 | 任务表字段类型
74 |
75 | :return: tuple
76 | """
77 | return 'INTEGER', 'TEXT', 'INTEGER'
78 |
79 | @property
80 | def Article(self) -> str:
81 | """
82 | Article -> str
83 | 文章表名
84 |
85 | :return: str
86 | """
87 | return 'article'
88 |
89 | @property
90 | def Article_Fields(self) -> tuple:
91 | """
92 | Article_Fields -> tuple
93 | 文章表字段
94 |
95 | :return: tuple
96 | """
97 | return 'ID', 'ITEM', 'LINK', 'ISREAD'
98 |
99 | @property
100 | def Article_Fields_Types(self) -> tuple:
101 | """
102 | Article_Fields_Types -> tuple
103 | 文章表字段类型
104 |
105 | :return: tuple
106 | """
107 | return 'INTEGER', 'TEXT', 'TEXT', 'INTEGER'
108 |
109 | @property
110 | def Video(self) -> str:
111 | """
112 | Video -> str
113 | 视频表名
114 |
115 | :return: str
116 | """
117 | return 'video'
118 |
119 | @property
120 | def Video_Fields(self) -> tuple:
121 | """
122 | Video_Fields -> tuple
123 | 视频表字段
124 |
125 | :return: tuple
126 | """
127 | return 'ID', 'ITEM', 'LINK', 'ISREAD'
128 |
129 | @property
130 | def Video_Fields_Types(self) -> tuple:
131 | """
132 | Video_Fields_Types -> tuple
133 | 视频表字段类型
134 |
135 | :return: tuple
136 | """
137 | return 'INTEGER', 'TEXT', 'TEXT', 'INTEGER'
138 |
139 | @property
140 | def Project(self) -> str:
141 | """
142 | Project -> str
143 | 专项答题表名
144 |
145 | :return: str
146 | """
147 | return 'project'
148 |
149 | @property
150 | def Project_Fields(self) -> tuple:
151 | """
152 | Project -> tuple
153 | 专项答题表字段
154 |
155 | :return:tuple
156 | """
157 | return 'ID', 'PID'
158 |
159 | @property
160 | def Project_Fields_Types(self) -> tuple:
161 | """
162 | Project_Fields_Types -> tuple
163 | 专项答题表字段类型
164 |
165 | :return: tuple
166 | """
167 | return 'INTEGER', 'INTEGER'
168 |
169 | @property
170 | def Baidu_AI(self) -> str:
171 | """
172 | Baidu_AI -> str
173 | 百度AI表名
174 |
175 | @return: str
176 | """
177 | return 'baidu_ai'
178 |
179 | @property
180 | def Baidu_AI_Fields(self) -> tuple:
181 | """
182 | Baidu_AI_Fields -> tuple
183 | 百度AI表字段名
184 |
185 | :return: tuple
186 | """
187 | return 'ID', 'AK', 'SK'
188 |
189 | @property
190 | def Baidu_AI_Fields_Types(self) -> tuple:
191 | """
192 | Baidu_AI_Fields_Types -> tuple
193 | 百度AI表字段类型
194 |
195 | :return: tuple
196 | """
197 | return 'INTEGER', 'TEXT', 'TEXT'
198 |
--------------------------------------------------------------------------------
/inside/DB/DB_Create.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Create.py
8 | # @Function : 数据库表创建
9 | from sqlite3 import Connection, Cursor
10 |
11 | from inside.DB.DB_Config import DB_CONFIG
12 | from inside.Template.Meta_Singleton import SINGLETON
13 |
14 | __all__ = ['DB_CREATE']
15 |
16 |
17 | class DB_CREATE(metaclass=SINGLETON):
18 | """数据库表创建操作类"""
19 |
20 | def __init__(self, connect: Connection, cursor: Cursor):
21 | """
22 | DB_CREATE(connect: Connection, cursor: Cursor)
23 | 初始化
24 |
25 | :param connect: 数据库连接对象
26 | :param cursor: 光标
27 | """
28 | self.__connect = connect
29 | self.__cursor = cursor
30 |
31 | def User(self) -> None:
32 | """
33 | User() -> None
34 | 用户表创建
35 |
36 | :return: None
37 | """
38 | name = DB_CONFIG().User
39 | fields = DB_CONFIG().User_Fields
40 | fields_types = DB_CONFIG().User_Fields_Types
41 | sql = f'''CREATE TABLE {name}
42 | ({fields[0]} {fields_types[0]} PRIMARY KEY AUTOINCREMENT,
43 | {fields[1]} {fields_types[1]} NOT NULL,
44 | {fields[2]} {fields_types[2]} NOT NULL,
45 | {fields[3]} {fields_types[3]} NOT NULL
46 | );'''
47 | self.__cursor.execute(sql)
48 | self.__connect.commit()
49 |
50 | def Task(self) -> None:
51 | """
52 | Task() -> None
53 | 任务表创建
54 |
55 | :return: None
56 | """
57 | name = DB_CONFIG().Task
58 | fields = DB_CONFIG().Task_Fields
59 | fields_types = DB_CONFIG().Task_Fields_Types
60 | sql = f'''CREATE TABLE {name}
61 | ({fields[0]} {fields_types[0]} PRIMARY KEY AUTOINCREMENT,
62 | {fields[1]} {fields_types[1]} NOT NULL,
63 | {fields[2]} {fields_types[2]} NOT NULL
64 | );'''
65 | self.__cursor.execute(sql)
66 | self.__connect.commit()
67 |
68 | def Article(self) -> None:
69 | """
70 | Article() -> None
71 | 文章表创建
72 |
73 | :return: None
74 | """
75 | name = DB_CONFIG().Article
76 | fields = DB_CONFIG().Article_Fields
77 | fields_types = DB_CONFIG().Article_Fields_Types
78 | sql = f'''CREATE TABLE {name}
79 | ({fields[0]} {fields_types[0]} PRIMARY KEY AUTOINCREMENT,
80 | {fields[1]} {fields_types[1]} NOT NULL,
81 | {fields[2]} {fields_types[2]} NOT NULL,
82 | {fields[3]} {fields_types[3]} NOT NULL
83 | );'''
84 | self.__cursor.execute(sql)
85 | self.__connect.commit()
86 |
87 | def Video(self) -> None:
88 | """
89 | Video() -> None
90 | 视频表创建
91 |
92 | :return: None
93 | """
94 | name = DB_CONFIG().Video
95 | fields = DB_CONFIG().Video_Fields
96 | fields_types = DB_CONFIG().Video_Fields_Types
97 | sql = f'''CREATE TABLE {name}
98 | ({fields[0]} {fields_types[0]} PRIMARY KEY AUTOINCREMENT,
99 | {fields[1]} {fields_types[1]} NOT NULL,
100 | {fields[2]} {fields_types[2]} NOT NULL,
101 | {fields[3]} {fields_types[3]} NOT NULL
102 | );'''
103 | self.__cursor.execute(sql)
104 | self.__connect.commit()
105 |
106 | def Project(self) -> None:
107 | """
108 | Project() -> None
109 | 专项答题表创建
110 |
111 | :return: None
112 | """
113 | name = DB_CONFIG().Project
114 | fields = DB_CONFIG().Project_Fields
115 | fields_types = DB_CONFIG().Project_Fields_Types
116 | sql = f'''CREATE TABLE {name}
117 | ({fields[0]} {fields_types[0]} PRIMARY KEY AUTOINCREMENT,
118 | {fields[1]} {fields_types[1]} NOT NULL
119 | );'''
120 | self.__cursor.execute(sql)
121 | self.__connect.commit()
122 |
123 | def Baidu_AI(self) -> None:
124 | """
125 | Baidu_AI() -> None
126 | 百度AI表创建
127 |
128 | :return: None
129 | """
130 | name = DB_CONFIG().Baidu_AI
131 | fields = DB_CONFIG().Baidu_AI_Fields
132 | fields_types = DB_CONFIG().Baidu_AI_Fields_Types
133 | sql = f'''CREATE TABLE {name}
134 | ({fields[0]} {fields_types[0]} PRIMARY KEY AUTOINCREMENT,
135 | {fields[1]} {fields_types[1]} NOT NULL,
136 | {fields[2]} {fields_types[2]} NOT NULL
137 | );'''
138 | self.__cursor.execute(sql)
139 | self.__connect.commit()
--------------------------------------------------------------------------------
/inside/DB/DB_Article.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Article.py
8 | # @Function : 文章表操作
9 | from sqlite3 import Connection, Cursor
10 | from typing import List
11 |
12 | from inside.DB.DB_Config import DB_CONFIG
13 | from inside.DB.Table_Class.Article import ARTICLE
14 | from inside.Template.Meta_Singleton import SINGLETON
15 |
16 | __all__ = ['DB_ARTICLE']
17 |
18 |
19 | class DB_ARTICLE(metaclass=SINGLETON):
20 | """文章表操作类"""
21 |
22 | def __init__(self, connect: Connection, cursor: Cursor):
23 | """
24 | 初始化
25 |
26 | :param connect: 数据库连接对象
27 | :param cursor: 数据库光标
28 | """
29 | self.__connect = connect
30 | self.__cursor = cursor
31 | self.__name = DB_CONFIG().Article
32 | self.__fields = DB_CONFIG().Article_Fields
33 |
34 | def Insert(self, article: ARTICLE) -> None:
35 | """
36 | Insert(article: ARTICLE) -> None
37 | 插入文章,如文章已存在则不进行操作
38 |
39 | :param article: 文章
40 | :return: None
41 | """
42 | if not self.Exist(article=article):
43 | sql = f'''INSERT INTO
44 | {self.__name} (
45 | {self.__fields[1]},
46 | {self.__fields[2]},
47 | {self.__fields[3]})
48 | VALUES (?, ?, ?);'''
49 | data = (
50 | article.Item,
51 | article.Link,
52 | article.Is_Read_DB
53 | )
54 | self.__cursor.execute(sql, data)
55 | self.__connect.commit()
56 |
57 | def Update(self, article: ARTICLE) -> None:
58 | """
59 | Update(article: ARTICLE) -> None
60 | 更新文章为已读,如文章不存在则不进行操作
61 |
62 | :param article: 文章
63 | :return: None
64 | """
65 | if self.Exist(article=article):
66 | sql = f'''UPDATE {self.__name}
67 | SET {self.__fields[3]}=1
68 | WHERE {self.__fields[1]}=?
69 | AND {self.__fields[2]}=?;'''
70 | data = (
71 | article.Item,
72 | article.Link
73 | )
74 | self.__cursor.execute(sql, data)
75 | self.__connect.commit()
76 |
77 | def Update_All(self) -> None:
78 | """
79 | Update_All() -> None
80 | 更新所有文章为未读
81 |
82 | :return: None
83 | """
84 | sql = f'''UPDATE {self.__name}
85 | SET {self.__fields[3]}=0;'''
86 | self.__cursor.execute(sql)
87 | self.__connect.commit()
88 |
89 | def Query(self, limit: int) -> List[ARTICLE]:
90 | """
91 | Query(limit: int) -> List[ARTICLE]
92 | 随机获取指定数量的未读文章
93 |
94 | :param limit: 数量
95 | :return: List[ARTICLE]
96 | """
97 | sql = f'''SELECT * FROM {self.__name}
98 | WHERE {self.__fields[3]}=0 ORDER BY random() LIMIT ?;'''
99 | data = (limit, )
100 | result = self.__cursor.execute(sql, data)
101 | temp = []
102 | for value in result:
103 | article = ARTICLE(
104 | item=value[1],
105 | link=value[2],
106 | isread=value[3]
107 | )
108 | temp.append(article)
109 | return temp
110 |
111 | def Exist_Enough(self, limit: int) -> bool:
112 | """
113 | Exist_Enough(limit: int) -> bool
114 | 是否存在指定数量的未读文章
115 |
116 | :param limit: 数量
117 | :return: bool
118 | """
119 | sql = f'''SELECT count() FROM {self.__name}
120 | WHERE {self.__fields[3]}=0 LIMIT ?;'''
121 | data = (limit, )
122 | result = self.__cursor.execute(sql, data)
123 | temp = next(result)
124 | return temp[0] >= limit
125 |
126 | def Exist(self, article: ARTICLE) -> bool:
127 | """
128 | Exist(article: ARTICLE) -> bool
129 | 是否存在指定的文章
130 |
131 | :param article: 文章
132 | :return: bool
133 | """
134 | sql = f'''SELECT 1 FROM {self.__name}
135 | WHERE {self.__fields[1]}=?
136 | AND {self.__fields[2]}=?;'''
137 | data = (
138 | article.Item,
139 | article.Link
140 | )
141 | result = self.__cursor.execute(sql, data)
142 | try:
143 | next(result)
144 | return True
145 | except StopIteration:
146 | return False
147 |
--------------------------------------------------------------------------------
/inside/Info/Get_Info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/23
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Get_Info.py
8 | # @Function : 获取用户信息
9 | from inside.Config.Api import API
10 | from inside.Info.Task_Info import TASK_INFO
11 | from inside.Info.User_Info import USER_INFO
12 | from inside.Template.Meta_Singleton import SINGLETON
13 | from inside.Tools.Requests import REQUESTS
14 | __all__ = ['GET_INFO']
15 |
16 |
17 | class GET_INFO(metaclass=SINGLETON):
18 | """获取信息类"""
19 |
20 | def __init__(self, token: str):
21 | """
22 | GET_INFO(token: str)
23 | 初始化
24 |
25 | :param token: 令牌
26 | """
27 | self.__cookie = {'token': token}
28 | self.__info = USER_INFO()
29 | self._Init_Info()
30 |
31 | def _Init_Info(self) -> None:
32 | """
33 | _Init_Info() -> None
34 | 初始化用户id
35 |
36 | :return: None
37 | """
38 | while True:
39 | try:
40 | html = REQUESTS().Get(
41 | url=API().Aggregate_Score.geturl(),
42 | cookies=self.__cookie
43 | )
44 | data = html.json()
45 | self.__info.User_Id = data['data']['userId']
46 | break
47 | except TypeError:
48 | continue
49 |
50 | def Get_Aggregate_Score(self) -> None:
51 | """
52 | Get_Aggregate_Score() -> None
53 | 获取总积分
54 |
55 | :return: None
56 | """
57 | while True:
58 | try:
59 | html = REQUESTS().Get(
60 | url=API().Aggregate_Score.geturl(),
61 | cookies=self.__cookie
62 | )
63 | data = html.json()
64 | self.__info.Aggregate_Score = data['data']['score']
65 | break
66 | except TypeError:
67 | continue
68 |
69 | def Get_Level(self) -> None:
70 | """
71 | Get_Level() -> None
72 | 获取等级
73 |
74 | :return: None
75 | """
76 | while True:
77 | try:
78 | html = REQUESTS().Get(
79 | url=API().Level.geturl(),
80 | cookies=self.__cookie
81 | )
82 | data = html.json()
83 | self.__info.Level = data['data']['level']
84 | self.__info.Level_Name = data['data']['levelName']
85 | self.__info.Rank_Accumulate_In_Country = \
86 | data['data']['rankAccumulateInCountry']
87 | break
88 | except TypeError:
89 | continue
90 |
91 | def Get_Daily_Score(self) -> None:
92 | """
93 | Get_Daily_Score() -> None
94 | 获取每日积分
95 |
96 | :return: None
97 | """
98 | while True:
99 | try:
100 | html = REQUESTS().Get(
101 | url=API().Daily_Score.geturl(),
102 | cookies=self.__cookie
103 | )
104 | data = html.json()
105 | self.__info.Daily_Score = data['data']['score']
106 | break
107 | except TypeError:
108 | continue
109 |
110 | def Get_Task_Bar(self) -> None:
111 | """
112 | Get_Task_Bar() -> None
113 | 获取任务进度
114 |
115 | :return: None
116 | """
117 | while True:
118 | try:
119 | html = REQUESTS().Get(
120 | url=API().Task_Bar.geturl(),
121 | cookies=self.__cookie
122 | )
123 | data = html.json()
124 | for rule in data['data']['dayScoreDtos']:
125 | task = self.__info.Task_Bar.get(rule['ruleId'])
126 | if not task:
127 | ruleId = rule['ruleId']
128 | name = rule['name']
129 | desc = rule['desc']
130 | currentScore = rule['currentScore']
131 | dayMaxScore = rule['dayMaxScore']
132 | task = TASK_INFO(
133 | ruleId=ruleId, name=name, desc=desc,
134 | currentScore=currentScore, dayMaxScore=dayMaxScore
135 | )
136 | else:
137 | task.Current_Score = rule['currentScore']
138 | self.__info.Update_Task_Bar_Info(task_info=task)
139 | break
140 | except TypeError:
141 | continue
142 |
--------------------------------------------------------------------------------
/inside/Driver/Driver_Init.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/16
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Driver_Init.py
8 | # @Function : 驱动初始化
9 | from selenium.webdriver import Chrome
10 | from selenium.webdriver.chrome.webdriver import WebDriver
11 |
12 | from inside.Options.Options import OPTIONS
13 | from inside.Config.Path import PATH
14 | from inside.Config.User_Agent import USER_AGENT
15 | from inside.Template.Meta_Singleton import SINGLETON
16 |
17 | __all__ = ['DRIVER_INIT']
18 |
19 |
20 | class DRIVER_INIT(metaclass=SINGLETON):
21 | """驱动初始化类"""
22 | __instances = {}
23 |
24 | def __init__(self):
25 | """初始化驱动配置文件"""
26 | self.__config = {
27 | 'browserName': 'chrome',
28 | # network日志相关
29 | 'loggingPrefs':
30 | {
31 | 'browser': 'ALL',
32 | 'driver': 'ALL',
33 | 'performance': 'ALL'
34 | },
35 | 'goog:chromeOptions':
36 | {
37 | 'prefs':
38 | {
39 | # 默认下载目录
40 | "download.default_directory": PATH().Temp,
41 | # 自动下载
42 | "download.prompt_for_download": False,
43 | # 无图模式
44 | "profile.managed_default_content_settings.images": 2
45 | },
46 | 'excludeSwitches': [
47 | # 不显示日志
48 | 'enable-logging',
49 | # 规避检测
50 | 'enable-automation'
51 | ],
52 | 'args':
53 | [
54 | # 浏览器标识
55 | '--user-agent=' + USER_AGENT().User_Agent,
56 | # 本地代理
57 | '--proxy-server=127.0.0.1:8080',
58 | # 忽略证书问题
59 | '--ignore-certificate-errors'
60 | ],
61 | 'perfLoggingPrefs':
62 | {
63 | # 开启network日志
64 | 'enableNetwork': True
65 | },
66 | 'w3c': False
67 | }
68 | }
69 | self.__Check_Options()
70 |
71 | def __Check_Options(self) -> None:
72 | """
73 | __Check_Options() -> None
74 | 检查选项,根据选项初始化配置文件
75 |
76 | :return: None
77 | """
78 | temp = []
79 | if OPTIONS().Mute_Audio:
80 | self.__config['goog:chromeOptions']['args'].append("--mute-audio")
81 | if OPTIONS().Headless:
82 | self.__config['goog:chromeOptions']['args'].append("--headless")
83 |
84 | @property
85 | def Task_Driver(self) -> WebDriver:
86 | """
87 | Task_Driver -> WebDriver
88 | 任务浏览器,根据配置文件生成
89 |
90 | :return: WebDriver
91 | """
92 | if not self.__instances.get('Task'):
93 | self.__instances['Task'] = Chrome(
94 | desired_capabilities=self.__config,
95 | executable_path=PATH().Driver_File
96 | )
97 | return self.__instances['Task']
98 |
99 | @property
100 | def Task_Quit(self) -> str:
101 | """
102 | Task_Quit -> str
103 | 任务浏览器退出
104 |
105 | :return: str
106 | Success: 退出成功
107 | Nonexistence: 不存在任务浏览器
108 | """
109 | if self.__instances.get('Task'):
110 | self.__instances['Task'].quit()
111 | self.__instances.pop('Task')
112 | return 'Success'
113 | return 'Nonexistence'
114 |
115 | @property
116 | def QR_Driver(self) -> WebDriver:
117 | """
118 | Task_Driver -> WebDriver
119 | 二维码浏览器,根据默认配置文件生成
120 |
121 | :return: WebDriver
122 | """
123 | if not self.__instances.get('QR'):
124 | self.__instances['QR'] = Chrome(
125 | executable_path=PATH().Driver_File
126 | )
127 | self.__instances['QR'].set_window_size(width=50, height=350)
128 | return self.__instances['QR']
129 |
130 | @property
131 | def QR_Quit(self) -> str:
132 | """
133 | Task_Quit -> str
134 | 二维码浏览器退出
135 |
136 | :return: str
137 | Success: 退出成功
138 | Nonexistence: 不存在任务浏览器
139 | """
140 | if self.__instances.get('QR'):
141 | self.__instances['QR'].quit()
142 | self.__instances.pop('QR')
143 | return 'Success'
144 | return 'Nonexistence'
145 |
--------------------------------------------------------------------------------
/inside/Options/Options.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/16
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Options_Manage.py
8 | # @Function : 选项
9 | from typing import Dict
10 |
11 | from inside.Template.Meta_Singleton import SINGLETON
12 |
13 | __all__ = ['OPTIONS']
14 |
15 |
16 | class OPTIONS(metaclass=SINGLETON):
17 | """选项类"""
18 |
19 | def __init__(self):
20 | """
21 | OPTIONS()
22 | 初始化,默认选项
23 |
24 | """
25 | self.__mute_audio = True
26 | self.__headless = True
27 | self.__token = True
28 | self.__baidu_ai = False
29 | self.__task_options = {
30 | 1: ['文章', False],
31 | 2: ['视频', False],
32 | 3: ['每日答题', False],
33 | 4: ['每周答题', False],
34 | 5: ['专项答题', False]
35 | }
36 |
37 | @property
38 | def Mute_Audio(self) -> bool:
39 | """
40 | Mute_Audio -> bool
41 | 获取禁音选项
42 |
43 | :return: bool
44 | """
45 | return self.__mute_audio
46 |
47 | @Mute_Audio.setter
48 | def Mute_Audio(self, on: bool) -> None:
49 | """
50 | Mute_Audio -> None
51 | 设置禁音选项
52 | 使用方法:Mute_Audio = True or False
53 |
54 | :param on: 开关
55 | :return: None
56 | """
57 | self.__mute_audio = on
58 |
59 | @property
60 | def Headless(self) -> bool:
61 | """
62 | Headless -> bool
63 | 获取无窗口化选项
64 |
65 | :return: bool
66 | """
67 | return self.__headless
68 |
69 | @Headless.setter
70 | def Headless(self, on: bool) -> None:
71 | """
72 | Headless -> None
73 | 设置无窗口化选项
74 | 使用方法:Headless = True or False
75 |
76 | :param on: 开关
77 | :return: None
78 | """
79 | self.__headless = on
80 |
81 | @property
82 | def Token(self) -> bool:
83 | """
84 | Token -> bool
85 | 获取持久化访问选项
86 |
87 | :return: bool
88 | """
89 | return self.__token
90 |
91 | @Token.setter
92 | def Token(self, on: bool) -> None:
93 | """
94 | Token -> None
95 | 设置持久化访问选项
96 | 使用方法:Token = True or False
97 |
98 | :param on: 开关
99 | :return: None
100 | """
101 | self.__token = on
102 |
103 | @property
104 | def Task_Options(self) -> Dict[int, str]:
105 | """
106 | Task_Options -> Dict[int, str]
107 | 任务选项,不暴露是否选择情况
108 |
109 | :return: Dict[int, str]
110 | """
111 | temp = {}
112 | for key, value in self.__task_options.items():
113 | temp[key] = value[0]
114 | return temp
115 |
116 | def Task_Option_Set_On(self, seq: int) -> None:
117 | """
118 | Task_Option_Set_On(seq: int) -> None
119 | 打开指定任务选项
120 |
121 | :param seq: 任务序号
122 | :return: None
123 | """
124 | self.__task_options[seq][-1] = True
125 |
126 | def Task_Option_Set_On_All(self) -> None:
127 | """
128 | Task_Option_Set_On_All() -> None
129 | 任务选项全选
130 |
131 | :return: None
132 | """
133 | for key in self.__task_options.keys():
134 | self.__task_options[key][-1] = True
135 |
136 | def Task_Option_Set_Off_All(self) -> None:
137 | """
138 | Task_Option_Set_Off_All() -> None
139 | 任务选项初始化
140 |
141 | :return: None
142 | """
143 | for key in self.__task_options.keys():
144 | self.__task_options[key][-1] = False
145 |
146 | @property
147 | def Article(self) -> bool:
148 | """
149 | Article -> bool
150 | 文章选项
151 |
152 | :return: bool
153 | """
154 | return self.__task_options[1][-1]
155 |
156 | @property
157 | def Video(self) -> bool:
158 | """
159 | Video -> bool
160 | 视频选项
161 |
162 | :return: bool
163 | """
164 | return self.__task_options[2][-1]
165 |
166 | @property
167 | def Daily_Answer(self) -> bool:
168 | """
169 | Daily_Answer -> bool
170 | 每日答题选项
171 |
172 | :return: bool
173 | """
174 | return self.__task_options[3][-1]
175 |
176 | @property
177 | def Weekly_Answer(self) -> bool:
178 | """
179 | Weekly_Answer -> bool
180 | 每周答题选项
181 |
182 | :return: bool
183 | """
184 | return self.__task_options[4][-1]
185 |
186 | @property
187 | def Project_Answer(self) -> bool:
188 | """
189 | Project_Answer -> bool
190 | 专项答题选项
191 |
192 | :return: bool
193 | """
194 | return self.__task_options[5][-1]
195 |
196 | @property
197 | def Baidu_AI(self) -> bool:
198 | """
199 | Baidu_AI -> bool
200 | 百度AI选项
201 |
202 | Returns: bool
203 |
204 | """
205 | return self.__baidu_ai
206 |
207 | @Baidu_AI.setter
208 | def Baidu_AI(self, on: bool) -> None:
209 | """
210 | Baidu_AI = on: bool
211 | 设置百度AI选项
212 |
213 | Args:
214 | on: bool
215 |
216 | Returns: None
217 |
218 | """
219 | self.__baidu_ai = on
220 |
--------------------------------------------------------------------------------
/inside/Info/User_Info.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/22
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : User_Info.py
8 | # @Function : 用户信息
9 | from typing import Dict
10 |
11 | from inside.Info.Task_Info import TASK_INFO
12 | from inside.Template.Meta_Singleton import SINGLETON
13 |
14 | __all__ = ['USER_INFO']
15 |
16 |
17 | class USER_INFO(metaclass=SINGLETON):
18 | """用户信息类"""
19 | __token: str
20 | __user_id: int
21 | __aggregate_score: float
22 | __daily_score: float
23 | __level: int
24 | __level_name: str
25 | __rank_accumulate_in_country: int
26 |
27 | __task_bar: Dict[int, TASK_INFO]
28 |
29 | def __init__(self):
30 | """
31 | USER_INFO()
32 | 初始化
33 |
34 | """
35 | self.__self = '_'+type(self).__name__
36 |
37 | @property
38 | def User_Id(self) -> int:
39 | """
40 | User_Id -> int
41 | 用户ID
42 |
43 | :return: int
44 | """
45 | if hasattr(self, self.__self+'__user_id'):
46 | return self.__user_id
47 | return -1
48 |
49 | @User_Id.setter
50 | def User_Id(self, user_id: int) -> None:
51 | """
52 | User_Id -> None
53 | 设置用户ID
54 |
55 | :param user_id: ID
56 | :return: None
57 | """
58 | if not hasattr(self, self.__self+'__user_id'):
59 | self.__user_id = user_id
60 |
61 | @property
62 | def Aggregate_Score(self) -> float:
63 | """
64 | Aggregate_Score -> float
65 | 总积分
66 |
67 | :return: float
68 | """
69 | if hasattr(self, self.__self+'__aggregate_score'):
70 | return self.__aggregate_score
71 | return -1
72 |
73 | @Aggregate_Score.setter
74 | def Aggregate_Score(self, aggregate_score: float) -> None:
75 | """
76 | Aggregate_Score -> None
77 | 设置总积分
78 |
79 | :param aggregate_score: 总积分
80 | :return: None
81 | """
82 | self.__aggregate_score = aggregate_score
83 |
84 | @property
85 | def Daily_Score(self) -> float:
86 | """
87 | Daily_Score -> float
88 | 每日积分
89 |
90 | :return: float
91 | """
92 | if hasattr(self, self.__self+'__daily_score'):
93 | return self.__daily_score
94 | return -1
95 |
96 | @Daily_Score.setter
97 | def Daily_Score(self, daily_score: float) -> None:
98 | """
99 | Daily_Score -> None
100 | 设置每日积分
101 |
102 | :param daily_score: 每日积分
103 | :return: None
104 | """
105 | self.__daily_score = daily_score
106 |
107 | @property
108 | def Level(self) -> int:
109 | """
110 | Level -> int
111 | 等级
112 |
113 | :return: int
114 | """
115 | if hasattr(self, self.__self+'__level'):
116 | return self.__level
117 | return -1
118 |
119 | @Level.setter
120 | def Level(self, level: int) -> None:
121 | """
122 | Level -> None
123 | 设置等级
124 |
125 | :param level: 等级
126 | :return: None
127 | """
128 | self.__level = level
129 |
130 | @property
131 | def Level_Name(self) -> str:
132 | """
133 | Level_Name -> str
134 | 段位
135 |
136 | :return: str
137 | """
138 | if hasattr(self, self.__self+'__level_name'):
139 | return self.__level_name
140 | return ''
141 |
142 | @Level_Name.setter
143 | def Level_Name(self, level_name: str) -> None:
144 | """
145 | Level_Name -> None
146 | 设置段位
147 |
148 | :param level_name: 段位
149 | :return: None
150 | """
151 | self.__level_name = level_name
152 |
153 | @property
154 | def Rank_Accumulate_In_Country(self) -> int:
155 | """
156 | Rank_Accumulate_In_Country -> int
157 | 全国排名
158 |
159 | :return: int
160 | """
161 | if hasattr(self, self.__self+'__rank_accumulate_in_country'):
162 | return self.__rank_accumulate_in_country
163 | return -1
164 |
165 | @Rank_Accumulate_In_Country.setter
166 | def Rank_Accumulate_In_Country(self, rank_accumulate_in_country: int) -> None:
167 | """
168 | Rank_Accumulate_In_Country -> None
169 | 设置全国排名
170 |
171 | :param rank_accumulate_in_country: 全国排名
172 | :return: None
173 | """
174 | self.__rank_accumulate_in_country = rank_accumulate_in_country
175 |
176 | @property
177 | def Task_Bar(self) -> Dict[int, TASK_INFO]:
178 | """
179 | Task_Bar -> Dict[int, TASK_INFO]
180 | 任务进度
181 |
182 | :return: Dict[int, TASK_INFO]
183 | """
184 | if hasattr(self, self.__self+'__task_bar'):
185 | return self.__task_bar
186 | else:
187 | return {}
188 |
189 | def Update_Task_Bar_Info(self, task_info: TASK_INFO) -> None:
190 | """
191 | Update_Task_Bar_Info(task_info: TASK_INFO) -> None
192 | 更新任务进度
193 |
194 | :param task_info: 任务类
195 | :return: None
196 | """
197 | if not hasattr(self, self.__self+'__task_bar'):
198 | self.__task_bar = {task_info.Rule_Id: task_info}
199 | else:
200 | self.__task_bar[task_info.Rule_Id] = task_info
201 |
--------------------------------------------------------------------------------
/inside/Config/SystemArgs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : SystemArgs.py
8 | # @Function : 系统参数
9 | from inside.Template.ABC_System_Args import SYSTEM_ARGS
10 |
11 | __all__ = ['WINDOWS', 'LINUX', 'MACOS']
12 |
13 |
14 | class WINDOWS(SYSTEM_ARGS):
15 | """Windows操作系统类"""
16 |
17 | def __init__(self):
18 | """
19 | WINDOWS()
20 | 初始化
21 |
22 | """
23 | import winreg
24 | self.__winreg = winreg
25 | self.__winreg_key = self.__winreg.OpenKey(
26 | key=self.__winreg.HKEY_CURRENT_USER,
27 | sub_key=r'Software\Google\Chrome\BLBeacon')
28 |
29 | @property
30 | def System(self) -> str:
31 | """
32 | System -> str
33 | 操作系统
34 |
35 | :return: str
36 | """
37 | return 'Windowns'
38 |
39 | @property
40 | def Clear(self) -> str:
41 | """
42 | Clear -> str
43 | 清空控制台命令
44 |
45 | :return: str
46 | """
47 | return 'cls'
48 |
49 | @property
50 | def Driver(self) -> str:
51 | """
52 | Driver -> str
53 | 驱动文件名称
54 |
55 | :return: str
56 | """
57 | return 'chromedriver.exe'
58 |
59 | @property
60 | def Driver_Chmod(self) -> str:
61 | """
62 | Driver_Chmod -> str
63 | 添加驱动文件执行权限命令
64 |
65 | :return: str
66 | """
67 | return ''
68 |
69 | @property
70 | def Chrome(self) -> bool:
71 | """
72 | Chrome -> bool
73 | 谷歌浏览器是否安装
74 |
75 | :return: bool
76 | """
77 | try:
78 | return isinstance(self.Chrome_Version, str)
79 | except FileNotFoundError:
80 | return False
81 |
82 | @property
83 | def Chrome_Version(self) -> str:
84 | """
85 | Chrome_Version -> str
86 | 谷歌浏览器版本号
87 |
88 | :return: str
89 | """
90 | return self.__winreg.QueryValueEx(
91 | self.__winreg_key,
92 | 'version'
93 | )[0]
94 |
95 |
96 | class LINUX(SYSTEM_ARGS):
97 | """Linux操作系统类"""
98 |
99 | def __init__(self):
100 | """
101 | LINUX()
102 | 初始化
103 |
104 | """
105 | import os
106 | self.__os = os
107 |
108 | @property
109 | def System(self) -> str:
110 | """
111 | System -> str
112 | 操作系统
113 |
114 | :return: str
115 | """
116 | return 'Linux'
117 |
118 | @property
119 | def Clear(self) -> str:
120 | """
121 | Clear -> str
122 | 清空控制台命令
123 |
124 | :return: str
125 | """
126 | return 'clear'
127 |
128 | @property
129 | def Driver(self) -> str:
130 | """
131 | Driver -> str
132 | 驱动文件名称
133 |
134 | :return: str
135 | """
136 | return 'chromedriver'
137 |
138 | @property
139 | def Driver_Chmod(self) -> str:
140 | """
141 | Driver_Chmod -> str
142 | 添加驱动文件执行权限命令
143 |
144 | :return: str
145 | """
146 | return 'chmod +x '
147 |
148 | @property
149 | def Chrome(self) -> bool:
150 | """
151 | Chrome -> bool
152 | 谷歌浏览器是否安装
153 |
154 | :return: bool
155 | """
156 | try:
157 | return isinstance(self.Chrome_Version, str)
158 | except IndexError:
159 | return False
160 |
161 | @property
162 | def Chrome_Version(self) -> str:
163 | """
164 | Chrome_Version -> str
165 | 谷歌浏览器版本号
166 |
167 | :return: str
168 | """
169 | version = self.__os.popen(cmd='google-chrome --version').readline()
170 | return version.strip().split()[-1]
171 |
172 |
173 | class MACOS(SYSTEM_ARGS):
174 | """MacOs操作系统类"""
175 |
176 | def __init__(self):
177 | """
178 | MACOS()
179 | 初始化
180 |
181 | """
182 | import os
183 | self.__os = os
184 |
185 | @property
186 | def System(self) -> str:
187 | """
188 | System -> str
189 | 操作系统
190 |
191 | :return: str
192 | """
193 | return 'macOs'
194 |
195 | @property
196 | def Clear(self) -> str:
197 | """
198 | Clear -> str
199 | 清空控制台命令
200 |
201 | :return: str
202 | """
203 | return 'clear'
204 |
205 | @property
206 | def Driver(self) -> str:
207 | """
208 | Driver -> str
209 | 驱动文件名称
210 |
211 | :return: str
212 | """
213 | return 'chromedriver'
214 |
215 | @property
216 | def Driver_Chmod(self) -> str:
217 | """
218 | Driver_Chmod -> str
219 | 添加驱动文件执行权限命令
220 |
221 | :return: str
222 | """
223 | return 'chmod +x '
224 |
225 | @property
226 | def Chrome(self) -> bool:
227 | """
228 | Chrome -> bool
229 | 谷歌浏览器是否安装
230 |
231 | :return: bool
232 | """
233 | try:
234 | return isinstance(self.Chrome_Version, str)
235 | except IndexError:
236 | return False
237 |
238 | @property
239 | def Chrome_Version(self) -> str:
240 | """
241 | Chrome_Version -> str
242 | 谷歌浏览器版本号
243 |
244 | :return: str
245 | """
246 | version = self.__os.popen(cmd=r'/Applications/Google\ '
247 | r'Chrome.app/Contents/MacOS/Google\ '
248 | r'Chrome --version').readline()
249 | return version.strip().split()[-1]
250 |
--------------------------------------------------------------------------------
/inside/Task/Task_Init.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/24
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Init.py
8 | # @Function : 任务初始化
9 | import base64
10 | import json
11 | from json.decoder import JSONDecodeError
12 | from typing import List
13 |
14 | from inside.Config.Api import API
15 | from inside.DB.DB_Manage import DB_MANAGE
16 | from inside.DB.Table_Class.Article import ARTICLE
17 | from inside.DB.Table_Class.Project import PROJECT
18 | from inside.DB.Table_Class.Task import TASK
19 | from inside.DB.Table_Class.Video import VIDEO
20 | from inside.Template.Meta_Singleton import SINGLETON
21 | from inside.Tools.Requests import REQUESTS
22 |
23 | __all__ = ['TASK_INIT']
24 |
25 |
26 | class TASK_INIT(metaclass=SINGLETON):
27 | """任务初始化类"""
28 |
29 | def __init__(self):
30 | """
31 | TASK_INIT()
32 | 初始化
33 |
34 | """
35 | self.__Init_Task()
36 |
37 | @classmethod
38 | def __Init_Task(cls) -> None:
39 | """
40 | 任务表初始化(不为空的时候)
41 |
42 | :return: None
43 | """
44 | if DB_MANAGE().Task.Empty():
45 | html = REQUESTS.Get(url=API().Task_Parent.geturl())
46 | parent = html.json()
47 | for key in parent.keys():
48 | son = API().Task_Son.geturl().format(channel_id=key)
49 | task = TASK(link=son, isread=False)
50 | DB_MANAGE().Task.Insert(task=task)
51 |
52 | @classmethod
53 | def Init_Article_Video(cls) -> None:
54 | """
55 | Init_Article_Video() -> None
56 | 从任务表中取出任务,进行解析并按类别填入文章表或视频表
57 |
58 | :return: None
59 | """
60 | if DB_MANAGE().Task.Exist_Enough():
61 | print("解析任务集中")
62 | task = DB_MANAGE().Task.Query()
63 | html = REQUESTS.Get(url=task.Link)
64 | DB_MANAGE().Task.Update(task=task)
65 | try:
66 | for temp in html.json():
67 | if temp['type'] == 'tuwen':
68 | print("解析为文章任务集")
69 | article = ARTICLE(
70 | item=temp['itemId'],
71 | link=temp['url'],
72 | isread=False
73 | )
74 | DB_MANAGE().Article.Insert(article=article)
75 | elif temp['type'] == 'shipin':
76 | print("解析为视频任务集")
77 | video = VIDEO(
78 | item=temp['itemId'],
79 | link=temp['url'],
80 | isread=False
81 | )
82 | DB_MANAGE().Video.Insert(video=video)
83 | else:
84 | print("解析为其他任务集")
85 | except JSONDecodeError:
86 | print("解析为干扰项")
87 | cls.Init_Article_Video()
88 |
89 | def Assigning_Article(self, num: int) -> List[ARTICLE]:
90 | """
91 | Assigning_Article(num: int) -> List[ARTICLE]
92 | 获取指定数量的文章任务
93 |
94 | :param num: 任务数
95 | :return: List[ARTICLE]
96 | """
97 | print("正在分发文章任务")
98 | if DB_MANAGE().Article.Exist_Enough(limit=num):
99 | return DB_MANAGE().Article.Query(limit=num)
100 | else:
101 | print("检测到文章任务数不足,自动获取任务集")
102 | self.Init_Article_Video()
103 | return self.Assigning_Article(num=num)
104 |
105 | def Assigning_Video(self, num: int) -> List[VIDEO]:
106 | """
107 | Assigning_Video(num: int) -> List[VIDEO]
108 | 获取指定数量的视频任务
109 |
110 | :param num: 任务数
111 | :return: List[VIDEO]
112 | """
113 | print("正在分发视频任务")
114 | if DB_MANAGE().Video.Exist_Enough(limit=num):
115 | return DB_MANAGE().Video.Query(limit=num)
116 | else:
117 | print("检测到视频任务数不足,自动获取任务集")
118 | self.Init_Article_Video()
119 | return self.Assigning_Video(num=num)
120 |
121 | def Assigning_Weekly_Answer(self, token: str) -> int:
122 | """
123 | Assigning_Weekly_Answer(token: str) -> int
124 | 获取本周最早且未做的每周答题任务ID
125 |
126 | :param token: 令牌
127 | :return: int
128 | """
129 | cookie = {'token': token}
130 | for num in range(3, 0, -1):
131 | html = REQUESTS.Get(
132 | url=API().Weekly_Answer_Topics.geturl().format(num=num),
133 | cookies=cookie
134 | )
135 | data = html.json()
136 | data = json.loads(base64.b64decode(data['data_str']).decode('utf-8'))
137 | for group in data['list'][::-1]:
138 | for practice in group['practices'][::-1]:
139 | if not practice['seeSolution']:
140 | return practice['id']
141 |
142 | def Assigning_Project_Answer(self, token: str) -> int:
143 | """
144 | Assigning_Project_Answer(token: str) -> int
145 | 获取最早且未过期的专项答题任务ID
146 |
147 | :param token: 令牌
148 | :return: int
149 | """
150 | cookie = {'token': token}
151 | for num in range(5, 0, -1):
152 | html = REQUESTS.Get(
153 | url=API().Project_Answer_Topics.geturl().format(num=num),
154 | cookies=cookie
155 | )
156 | data = html.json()
157 | data = json.loads(base64.b64decode(data['data_str']).decode('utf-8'))
158 | for topic in data['list'][::-1]:
159 | if (not topic['overdue']) and (not topic['seeSolution']):
160 | if not DB_MANAGE().Project.Exist(project=PROJECT(pid=topic['id'])):
161 | return topic['id']
162 |
--------------------------------------------------------------------------------
/inside/Task/Mitmdump/Intercept/Project.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/2/6
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Project.py
8 | # @Function :
9 | import base64
10 | import json
11 | import re
12 | from random import randint
13 | from typing import Dict, List
14 |
15 | from inside.Task.Mitmdump.Intercept.ABC_Answer import ABC_ANSWER
16 |
17 | from inside.Baidu_AI.Baidu_AI_Manage import BAIDU_AI_MANAGE
18 | from inside.Config.Path import PATH
19 | __all__ = ['PROJECT']
20 |
21 |
22 | class PROJECT(ABC_ANSWER):
23 | """专项答题拦截体"""
24 |
25 | def Url_Put(self, url: str) -> bool:
26 | """
27 | Url_Put(url: str) -> bool
28 | 提交判断
29 |
30 | :param url: url
31 | :return: bool
32 | """
33 | return "https://pc-proxy-api.xuexi.cn/api/exam/service/detail/submitV3" \
34 | == url
35 |
36 | def Url_Res(self, url: str) -> bool:
37 | """
38 | Url_Res(url: str) -> bool
39 | 答案判断
40 |
41 | :param url: url
42 | :return: bool
43 | """
44 | res = "https://pc-proxy-api.xuexi.cn/api/exam/service/detail" \
45 | "/queryV3\\?type=1&id=.*&forced=true"
46 | if re.match(res, url):
47 | return True
48 | return False
49 |
50 | def Extract(self, data: bytes) -> Dict:
51 | """
52 | Extract(data: bytes) -> Dict
53 | 提取答案
54 |
55 | :param data: 包含答案的字节数据
56 | :return: Dict
57 | """
58 | data = data.decode('utf-8')
59 | data = json.loads(data)['data_str']
60 | return json.loads(base64.b64decode(data).decode('utf-8'))
61 |
62 | def __Answer(self, res: Dict) -> List[Dict]:
63 | """
64 | __Answer(res: Dict) -> List[Dict]
65 | 由于专项答题没有答案,判题是在云端,所以需要分析答案
66 |
67 | :param res: 包含提示的题目
68 | :return: List[Dict]
69 | """
70 | res_type = res['questionDisplay']
71 | temp = []
72 | options = re.findall(r".*?",
73 | res['questionDesc'])
74 | for index, value in enumerate(options):
75 | options[index] = value.split('>')[1].split('<')[0]
76 | if res_type in (1, 2):
77 | for option in options:
78 | for answer in res['answers']:
79 | if option in answer['content'] or answer['content'] in option:
80 | tp = {
81 | 'answerId': answer['answerId'],
82 | 'value': answer['label']
83 | }
84 | if tp not in temp:
85 | temp.append(tp)
86 | if not temp:
87 | for answer in res['answers']:
88 | if answer['content'] in res['questionDesc']:
89 | tp = {
90 | 'answerId': answer['answerId'],
91 | 'value': answer['label']
92 | }
93 | if tp not in temp:
94 | temp.append(tp)
95 | elif res_type == 4:
96 | with open(PATH().Baidu_AI_On, 'r', encoding='utf-8') as f:
97 | on = f.read()
98 | f.close()
99 | if on == '1':
100 | if res['questionDesc'] == '请观看视频':
101 | body = res['body']
102 | value = BAIDU_AI_MANAGE.Tools().Answer(video_link=res['videoUrl'])
103 | print('检测到视频题目,由于专项答题答案匹配目前还未完善,所以需要手动填入答案')
104 | print(f"题目部分:{body[body.index('()')-len(value):]}")
105 | print(f"答案为:{value}")
106 | print("请从答案中提取个数与()个数一致的答案,以空格分隔,注意顺序")
107 | while True:
108 | daan = input(":").strip().split()
109 | check = [ck for ck in daan if ck in value]
110 | if len(check) != len(daan) or not daan:
111 | print("输入非答案,重新输入!")
112 | continue
113 | break
114 | for index, answer in enumerate(res['answers']):
115 | temp.append({
116 | 'answerId': answer['answerId'],
117 | 'value': daan[index]
118 | })
119 | else:
120 | num = res['body'].count('()')
121 | if num == 1:
122 | tp = ''
123 | for option in options:
124 | tp += option
125 | temp.append(
126 | {
127 | 'answerId': res['answers'][0]['answerId'],
128 | 'value': tp
129 | }
130 | )
131 | else:
132 | for index, option in enumerate(options):
133 | temp.append(
134 | {
135 | 'answerId': res['answers'][index]['answerId'],
136 | 'value': option
137 | }
138 | )
139 | return temp
140 |
141 | def Inject(self, data: bytes, res: Dict) -> bytes:
142 | """
143 | Inject(data: bytes, res: Dict) -> bytes
144 | 注入答案
145 |
146 | :param data: 原始提交数据
147 | :param res: 包含答案数据
148 | :return: 修改后的提交数据
149 | """
150 | data = json.loads(data.decode('utf-8'))
151 | data['uniqueId'] = res['uniqueId']
152 | data['questions'].clear()
153 | data['usedTime'] = randint(20, 50)
154 | for question in res['questions']:
155 | data['questions'].append(
156 | {
157 | 'questionId': question['questionId'],
158 | 'answers': self.__Answer(res=question)
159 | }
160 | )
161 | return json.dumps(data).encode('utf-8')
--------------------------------------------------------------------------------
/inside/Task/Task_Answer.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/25
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Answer.py
8 | # @Function : 答题任务
9 | import re
10 | import time
11 |
12 | from tqdm import tqdm
13 | from typing import List
14 |
15 | from selenium.common.exceptions import StaleElementReferenceException, \
16 | ElementClickInterceptedException, TimeoutException, \
17 | NoAlertPresentException, NoSuchElementException
18 | from selenium.webdriver.chrome.webdriver import WebDriver
19 |
20 | from inside.Config.Api import API
21 | from inside.Template.Meta_Singleton import SINGLETON
22 | from inside.Template.Task_Exception import TASK_EXCEPTION
23 | from inside.Tools.Network import NETWORK
24 |
25 | from selenium.webdriver.remote.webelement import WebElement
26 | from selenium.webdriver.support import expected_conditions as EC
27 | from selenium.webdriver.support.wait import WebDriverWait
28 | from selenium.webdriver.common.by import By
29 |
30 | __all__ = ['TASK_ANSWER']
31 |
32 |
33 | class TASK_ANSWER(metaclass=SINGLETON):
34 | """答题任务类"""
35 |
36 | def __init__(self, driver: WebDriver):
37 | """
38 | TASK_ANSWER(driver: WebDriver)
39 | 初始化
40 |
41 | :param driver: 驱动
42 | """
43 | self.__driver = driver
44 | self.__network = NETWORK()
45 | self.__wait = WebDriverWait(self.__driver, 10)
46 |
47 | def Topic_Type(self) -> str:
48 | """
49 | Topic_Type() -> str
50 | 题目类型
51 |
52 | :return: str
53 | """
54 | topic_type_ec = EC.presence_of_element_located(
55 | (
56 | By.CLASS_NAME, 'q-header'
57 | )
58 | )
59 | topic_type: WebElement = self.__wait.until(topic_type_ec)
60 | return topic_type.text.strip()
61 |
62 | def Topic_Seq(self) -> str:
63 | """
64 | Topic_Seq() -> str
65 | 题目序号,返回'x/x'
66 |
67 | :return: str
68 | """
69 | topic_seq_ec = EC.presence_of_element_located(
70 | (
71 | By.CLASS_NAME, 'pager'
72 | )
73 | )
74 | topic_seq: WebElement = self.__wait.until(topic_seq_ec)
75 | return topic_seq.text.strip()
76 |
77 | def Topic_Input(self) -> List[WebElement]:
78 | """
79 | Topic_Input() -> WebElement
80 | 填空题输入框
81 |
82 | :return: WebElement
83 | """
84 | topic_input_ec = EC.presence_of_all_elements_located(
85 | (
86 | By.TAG_NAME, 'input'
87 | )
88 | )
89 | return self.__wait.until(topic_input_ec)
90 |
91 | def Topic_Options(self) -> List[WebElement]:
92 | """
93 | Topic_Options() -> List[WebElement]
94 | 选择题选项
95 |
96 | :return: List[WebElement]
97 | """
98 | topic_options_ec = EC.presence_of_element_located(
99 | (
100 | By.CLASS_NAME, 'q-answers'
101 | )
102 | )
103 | topic_options: WebElement = self.__wait.until(topic_options_ec)
104 | return topic_options.find_elements_by_tag_name(name='div')
105 |
106 | def Topic_Submit(self) -> WebElement:
107 | """
108 | Topic_Submit() -> WebElement
109 | 题目提交按钮
110 |
111 | :return: WebElement
112 | """
113 | topic_submits_ec = EC.presence_of_element_located(
114 | (
115 | By.CLASS_NAME, 'action-row'
116 | )
117 | )
118 | topic_submits: WebElement = self.__wait.until(topic_submits_ec)
119 | submits = topic_submits.find_elements_by_tag_name(name='button')
120 | for submit in submits:
121 | if submit.is_enabled():
122 | return submit
123 |
124 | def __Accomplish(self) -> bool:
125 | """
126 | __Accomplish() -> bool
127 | 检测答题是否完成
128 |
129 | :return: bool
130 | """
131 | while True:
132 | for key, value in self.__network.Get().items():
133 | if re.match(
134 | pattern=API().Answer_Accomplish.geturl(),
135 | string=key
136 | ):
137 | return True
138 |
139 | def __Error(self) -> bool:
140 | """
141 | __Error() -> bool
142 | 检测是否有弹窗
143 |
144 | :return: bool
145 | """
146 | try:
147 | time.sleep(1)
148 | self.__driver.find_element_by_class_name(
149 | name='ant-modal-content')
150 | return True
151 | except NoSuchElementException:
152 | return False
153 |
154 | def __Alert(self) -> None:
155 | """
156 | __Alert() -> None
157 | 检测弹窗,并点击确认
158 |
159 | :return: None
160 | """
161 | try:
162 | self.__driver.switch_to.alert.accept()
163 | except NoAlertPresentException:
164 | pass
165 |
166 | def __Do(self) -> None:
167 | """
168 | __Do() -> None
169 | 做一个题目
170 |
171 | :return: None
172 | """
173 | topic_type = self.Topic_Type()
174 | if '填空题' in topic_type:
175 | for answer in self.Topic_Input():
176 | answer.send_keys('1')
177 | elif [x for x in ('单选题', '多选题') if x in topic_type]:
178 | for answer in self.Topic_Options():
179 | while answer.get_attribute(name='class') == 'q-answer choosable':
180 | answer.click()
181 | time.sleep(0.1)
182 | topic_seq = self.Topic_Seq()
183 | while self.Topic_Seq() == topic_seq:
184 | topic_submit = self.Topic_Submit()
185 | topic_submit.click()
186 | if self.__Error():
187 | raise TASK_EXCEPTION('失败')
188 |
189 | def Do(self, link: str) -> None:
190 | """
191 | Do(link: str) -> None
192 | 答题
193 |
194 | :param link: 题目链接
195 | :return: None
196 | """
197 | self.__network.Clear()
198 | self.__driver.get(url=link)
199 | self.__Alert()
200 | seq = int(self.Topic_Seq().split('/')[-1])
201 | bar = tqdm(
202 | desc='答题',
203 | total=seq,
204 | unit='题',
205 | leave=False,
206 | ncols=70
207 | )
208 | while True:
209 | try:
210 | self.__Do()
211 | bar.update(n=1)
212 | except (StaleElementReferenceException,
213 | ElementClickInterceptedException,
214 | TimeoutException):
215 | self.__Accomplish()
216 | bar.update(n=1)
217 | time.sleep(0.1)
218 | break
219 | bar.close()
220 |
--------------------------------------------------------------------------------
/inside/Task/Task_Manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/25
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Task_Manage.py
8 | # @Function : 任务管理器
9 | import time
10 |
11 | from tqdm import tqdm
12 | from selenium.webdriver.chrome.webdriver import WebDriver
13 |
14 | from inside.Config.Api import API
15 | from inside.DB.DB_Manage import DB_MANAGE
16 | from inside.DB.Table_Class.Project import PROJECT
17 | from inside.Info.Info_Manage import INFO_MANAGE
18 | from inside.Options.Options import OPTIONS
19 | from inside.Task.Task_Answer import TASK_ANSWER
20 | from inside.Task.Task_Article_Video import TASK_ARTICLE_VIDEO
21 | from inside.Task.Task_Init import TASK_INIT
22 | from inside.Template.Meta_Singleton import SINGLETON
23 | from inside.Template.Task_Exception import TASK_EXCEPTION
24 | from inside.Tools.Output import OUTPUT
25 |
26 | __all__ = ['TASK_MANAGE']
27 |
28 |
29 | class TASK_MANAGE(metaclass=SINGLETON):
30 | """任务管理类"""
31 |
32 | def __init__(self, driver: WebDriver):
33 | """
34 | TASK_MANAGE(driver: WebDriver)
35 | 初始化
36 |
37 | :param driver: 驱动
38 | """
39 | self.__driver = driver
40 | self.__answer_time = (time.time(), False)
41 |
42 | def __Article(self, num: int, tq: int) -> None:
43 | """
44 | __Article(num: int) -> None
45 | 进行文章任务
46 |
47 | :param num: 任务数量
48 | :return: None
49 | """
50 | temp = TASK_ARTICLE_VIDEO(task_driver=self.__driver)
51 | tasks = TASK_INIT().Assigning_Article(num=num)
52 | bar = tqdm(
53 | desc='文章',
54 | total=num,
55 | unit='it',
56 | leave=False,
57 | ncols=70
58 | )
59 | for task in tasks:
60 | temp.Do(task=task, tq=tq)
61 | DB_MANAGE().Article.Update(article=task)
62 | bar.update(n=1)
63 | OUTPUT.Info()
64 | bar.close()
65 |
66 | def __Video(self, num: int, tq: int) -> None:
67 | """
68 | __Video(num: int) -> None
69 | 进行视频任务
70 |
71 | :param num: 任务数量
72 | :return: None
73 | """
74 | temp = TASK_ARTICLE_VIDEO(task_driver=self.__driver)
75 | tasks = TASK_INIT().Assigning_Video(num=num)
76 | bar = tqdm(
77 | desc='视频',
78 | total=num,
79 | unit='it',
80 | leave=False,
81 | ncols=70
82 | )
83 | for task in tasks:
84 | temp.Do(task=task, tq=tq)
85 | DB_MANAGE().Video.Update(video=task)
86 | bar.update(n=1)
87 | OUTPUT.Info()
88 | bar.close()
89 |
90 | def __Check_Article(self) -> None:
91 | """
92 | __Check_Article() -> None
93 | 监测文章任务的完成
94 |
95 | :return: None
96 | """
97 | while True:
98 | bar = INFO_MANAGE().Task_Bar
99 | p = bar[1].Day_Max_Score - bar[1].Current_Score
100 | c = bar[1002].Day_Max_Score - bar[1002].Current_Score
101 | if p and not c:
102 | self.__Article(num=p, tq=1)
103 | elif p and c:
104 | self.__Article(num=p, tq=(c*4)//p)
105 | elif not p and c:
106 | self.__Article(num=1, tq=c*4)
107 | else:
108 | break
109 |
110 | def __Check_Video(self) -> None:
111 | """
112 | __Check_Video() -> None
113 | 监测视频任务的完成
114 |
115 | :return: None
116 | """
117 | while True:
118 | bar = INFO_MANAGE().Task_Bar
119 | p = bar[2].Day_Max_Score - bar[2].Current_Score
120 | c = bar[1003].Day_Max_Score - bar[1003].Current_Score
121 | if p and not c:
122 | self.__Video(num=p, tq=1)
123 | elif p and c:
124 | self.__Video(num=p, tq=(c*4)//p)
125 | elif not p and c:
126 | self.__Video(num=1, tq=c*4)
127 | else:
128 | break
129 |
130 | def __Check_Daily_Answer(self) -> None:
131 | """
132 | __Check_Daily_Answer() -> None
133 | 监测每日答题任务的完成
134 |
135 | :return: None
136 | """
137 | while True:
138 | bar = INFO_MANAGE().Task_Bar
139 | if bar[6].Current_Score != bar[6].Day_Max_Score:
140 | if self.__answer_time[-1]:
141 | if time.time() - self.__answer_time[0] <= 10:
142 | continue
143 | temp = TASK_ANSWER(driver=self.__driver)
144 | temp.Do(link=API().Daily_Answer.geturl())
145 | OUTPUT.Info()
146 | self.__answer_time = (time.time(), True)
147 | else:
148 | break
149 |
150 | def __Check_Weekly_Answer(self) -> None:
151 | """
152 | __Check_Weekly_Answer() -> None
153 | 监测每周答题任务的完成
154 |
155 | :return: None
156 | """
157 | while True:
158 | bar = INFO_MANAGE().Task_Bar
159 | if bar[5].Current_Score != bar[5].Day_Max_Score:
160 | if self.__answer_time[-1]:
161 | if time.time() - self.__answer_time[0] <= 10:
162 | continue
163 | token = self.__driver.get_cookie(name='token')['value']
164 | iid = TASK_INIT().Assigning_Weekly_Answer(token=token)
165 | if not iid:
166 | print("没有每周答题任务了")
167 | break
168 | temp = TASK_ANSWER(driver=self.__driver)
169 | temp.Do(link=API().Weekly_Answer_Topic.geturl().format(num=iid))
170 | OUTPUT.Info()
171 | self.__answer_time = (time.time(), True)
172 | else:
173 | break
174 |
175 | def __Check_Project_Answer(self) -> None:
176 | """
177 | __Check_Project_Answer() -> None
178 | 监测专项答题任务的完成
179 |
180 | :return: None
181 | """
182 | while True:
183 | bar = INFO_MANAGE().Task_Bar
184 | if bar[4].Current_Score != bar[4].Day_Max_Score:
185 | if self.__answer_time[-1]:
186 | if time.time() - self.__answer_time[0] <= 10:
187 | continue
188 | token = self.__driver.get_cookie(name='token')['value']
189 | iid = TASK_INIT().Assigning_Project_Answer(token=token)
190 | if not iid:
191 | print("没有专项答题任务了")
192 | break
193 | temp = TASK_ANSWER(driver=self.__driver)
194 | try:
195 | temp.Do(link=API().Project_Answer_Topic.geturl().format(num=iid))
196 | except TASK_EXCEPTION:
197 | DB_MANAGE().Project.Insert(PROJECT(pid=iid))
198 | OUTPUT.Info()
199 | self.__answer_time = (time.time(), True)
200 | else:
201 | break
202 |
203 | def Task(self) -> None:
204 | """
205 | Task() -> None
206 | 根据选项执行相应的任务
207 |
208 | :return: None
209 | """
210 | if OPTIONS().Article:
211 | self.__Check_Article()
212 | if OPTIONS().Video:
213 | self.__Check_Video()
214 | if OPTIONS().Daily_Answer:
215 | self.__Check_Daily_Answer()
216 | if OPTIONS().Weekly_Answer:
217 | self.__Check_Weekly_Answer()
218 | if OPTIONS().Project_Answer:
219 | self.__Check_Project_Answer()
220 |
--------------------------------------------------------------------------------
/inside/Driver/Driver_Analysis.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/17
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Driver_Analysis.py
8 | # @Function : 驱动解析
9 | import requests
10 | from urllib.parse import urlparse, ParseResult
11 | from bs4 import BeautifulSoup, ResultSet
12 |
13 | from inside.Config.System import SYSTEM
14 | from inside.Template.ABC_Driver_Analysis import DRIVER_ANALYSIS
15 | __all__ = ['GOOGLEAPIS_DRIVER_ANALYSIS', 'TAOBAO_DRIVER_ANALYSIS']
16 |
17 |
18 | class GOOGLEAPIS_DRIVER_ANALYSIS(DRIVER_ANALYSIS):
19 | """驱动官网下载类"""
20 |
21 | @property
22 | def Master(self) -> ParseResult:
23 | """
24 | Master -> ParseResult
25 | 官网
26 |
27 | :return: ParseResult
28 | """
29 | return urlparse(
30 | url="http://chromedriver.storage.googleapis.com/index.html"
31 | )
32 |
33 | def __Master(self) -> ParseResult:
34 | """
35 | __Master() -> ParseResult
36 | 官网xml列表
37 |
38 | :return: ParseResult
39 | """
40 | return urlparse(
41 | url="http://chromedriver.storage.googleapis.com/?delimiter"
42 | "=/&prefix="
43 | )
44 |
45 | def __Definite(self) -> ParseResult:
46 | """
47 | __Definite() -> ParseResult
48 | 具体版本xml列表
49 |
50 | :return: ParseResult
51 | """
52 | return urlparse(
53 | url="http://chromedriver.storage.googleapis.com/?delimiter=/&prefix={version}/"
54 | )
55 |
56 | def __Download_Link(self) -> ParseResult:
57 | """
58 | __Download_Link() -> ParseResult
59 | 驱动下载链接
60 |
61 | :return: ParseResult
62 | """
63 | return urlparse(
64 | url="http://chromedriver.storage.googleapis.com/{path}"
65 | )
66 |
67 | def __All_Driver(self) -> ResultSet:
68 | """
69 | __All_Driver() -> ResultSet
70 | 所有版本xml列表
71 |
72 | :return: ResultSet
73 | """
74 | html = requests.get(url=self.__Master().geturl())
75 | for retry in range(3):
76 | if html.status_code == 200:
77 | break
78 | html = requests.get(url=self.__Master().geturl())
79 | html.encoding = html.apparent_encoding
80 | soup = BeautifulSoup(html.text, 'lxml')
81 | return soup.select(selector='CommonPrefixes')
82 |
83 | def __Filtrate_Version(self, chrome_version: str) -> str:
84 | """
85 | __Filtrate_Version(chrome_version: str) -> str
86 | 匹配驱动版本
87 |
88 | :param chrome_version: 谷歌浏览器版本号
89 | :return: str
90 | """
91 | res = []
92 | chrome_version = chrome_version.split('.')[:-1]
93 | for commonprefixes in self.__All_Driver():
94 | temp = commonprefixes.text
95 | if temp.split('.')[:-1] == chrome_version:
96 | res.append(temp.strip('/'))
97 | return max(res, key=lambda x: int(x.split('.')[-1]))
98 |
99 | def __Select_Driver(self, driver_version: str) -> ResultSet:
100 | """
101 | __Select_Driver(driver_version: str) -> ResultSet
102 | 获取具体版本驱动xml列表
103 |
104 | :param driver_version: 驱动版本号
105 | :return: ResultSet
106 | """
107 | html = requests.get(
108 | url=self.__Definite().geturl().format(version=driver_version)
109 | )
110 | for retry in range(3):
111 | if html.status_code == 200:
112 | break
113 | html = requests.get(
114 | url=self.__Definite().geturl().format(version=driver_version)
115 | )
116 | html.encoding = html.apparent_encoding
117 | soup = BeautifulSoup(html.text, 'lxml')
118 | return soup.select(selector='Contents')
119 |
120 | def __Filtrate_Driver(self, system: SYSTEM, soups: ResultSet) -> str:
121 | """
122 | __Filtrate_Driver(system: SYSTEM, soups: ResultSet) -> str
123 | 获取与系统匹配的驱动
124 |
125 | :param system: 系统类
126 | :param soups: 驱动xml列表
127 | :return: str
128 | """
129 | res = []
130 | Os = self._map.get(system.Name)
131 | for contents in soups:
132 | temp = contents.key.text
133 | if Os in temp:
134 | res.append(temp)
135 | if len(res) == 1:
136 | return res[0]
137 | else:
138 | t = [x for x in res if str(system.Bit) in x]
139 | if len(t) == 1:
140 | return t[0]
141 | return min(res, key=len)
142 |
143 | def Download(self, system: SYSTEM) -> ParseResult:
144 | """
145 | Download(system: SYSTEM) -> ParseResult
146 | 获取驱动下载链接
147 |
148 | :param system: 系统类
149 | :return: ParseResult
150 | """
151 | driver_version = self.__Filtrate_Version(
152 | chrome_version=system.Chrome_Version)
153 | soups = self.__Select_Driver(driver_version=driver_version)
154 | path = self.__Filtrate_Driver(system=system, soups=soups)
155 | return urlparse(
156 | url=self.__Download_Link().geturl().format(path=path)
157 | )
158 |
159 |
160 | class TAOBAO_DRIVER_ANALYSIS(DRIVER_ANALYSIS):
161 | """淘宝镜像类"""
162 |
163 | @property
164 | def Master(self) -> ParseResult:
165 | """
166 | Master -> ParseResult
167 | 官网
168 |
169 | :return: ParseResult
170 | """
171 | return urlparse(
172 | url="http://npm.taobao.org/mirrors/chromedriver/"
173 | )
174 |
175 | def __Defininite(self) -> ParseResult:
176 | """
177 | __Definite() -> ParseResult
178 | 具体版本列表
179 |
180 | :return: ParseResult
181 | """
182 | return urlparse(
183 | url="http://npm.taobao.org{path}"
184 | )
185 |
186 | def __Download_link(self) -> ParseResult:
187 | """
188 | __Download_link() -> ParseResult
189 | 驱动下载链接
190 |
191 | :return: ParseResult
192 | """
193 | return urlparse(
194 | url="https://cdn.npm.taobao.org/dist{path}"
195 | )
196 |
197 | def __All_Driver(self) -> ResultSet:
198 | """
199 | __All_Driver() -> ResultSet
200 | 所有驱动列表
201 |
202 | :return: ResultSet
203 | """
204 | html = requests.get(url=self.Master.geturl())
205 | for retry in range(3):
206 | if html.status_code == 200:
207 | break
208 | html = requests.get(url=self.Master.geturl())
209 | html.encoding = html.apparent_encoding
210 | soup = BeautifulSoup(html.text, 'html.parser')
211 | return soup.pre.select(selector='a')
212 |
213 | def __Filtrate_Version(self, chrome_version: str) -> str:
214 | """
215 | __Filtrate_Version(chrome_version: str) -> str
216 | 获取与谷歌浏览器版本匹配的驱动版本
217 |
218 | :param chrome_version: 谷歌浏览器版本号
219 | :return: str
220 | """
221 | res = []
222 | chrome_version = chrome_version.split('.')[:-1]
223 | for a in self.__All_Driver():
224 | temp = a.text.split('.')[:-1]
225 | if temp == chrome_version:
226 | res.append(a)
227 | return max(res, key=lambda x: int(x.text.split('.')[-1].strip('/'))).attrs.get('href')
228 |
229 | def __Select_Driver(self, drivers_link: ParseResult) -> ResultSet:
230 | """
231 | __Select_Driver(drivers_link: ParseResult) -> ResultSet
232 | 具体版本号驱动列表
233 |
234 | :param drivers_link: 具体版本号驱动链接
235 | :return: ResultSet
236 | """
237 | html = requests.get(url=drivers_link.geturl())
238 | for retry in range(3):
239 | if html.status_code == 200:
240 | break
241 | html = requests.get(url=drivers_link.geturl())
242 | html.encoding = html.apparent_encoding
243 | soup = BeautifulSoup(html.text, 'html.parser')
244 | return soup.pre.select(selector='a')
245 |
246 | def __Filtrate_Driver(self, system: SYSTEM, soups: ResultSet) -> str:
247 | """
248 | __Filtrate_Driver(system: SYSTEM, soups: ResultSet) -> str
249 | 获取与系统匹配的驱动
250 |
251 | :param system: 系统类
252 | :param soups: 具体版本号驱动列表
253 | :return: str
254 | """
255 | res = []
256 | Os = self._map.get(system.Name)
257 | for a in soups:
258 | if Os in a.text:
259 | res.append(a)
260 | if len(res) == 1:
261 | return res[0].attrs.get('href')
262 | else:
263 | t = [x for x in res if str(system.Bit) in x.text]
264 | if len(t) == 1:
265 | return t[0].attrs.get('href')
266 | return min(res, key=lambda x: len(x.text)).attrs.get('href')
267 |
268 | def Download(self, system: SYSTEM) -> ParseResult:
269 | """
270 | Download(system: SYSTEM) -> ParseResult
271 | 获取驱动下载链接
272 |
273 | :param system: 系统类
274 | :return: ParseResult
275 | """
276 | driver_version = self.__Filtrate_Version(
277 | chrome_version=system.Chrome_Version
278 | )
279 | driver_version = self.__Defininite().geturl().format(path=driver_version)
280 | driver_version = urlparse(url=driver_version)
281 | soups = self.__Select_Driver(drivers_link=driver_version)
282 | path = self.__Filtrate_Driver(system=system, soups=soups)
283 | index = path[1:].find('/')+1
284 | return urlparse(
285 | url=self.__Download_link().geturl().format(path=path[index:])
286 | )
287 |
--------------------------------------------------------------------------------
/inside/DB/DB_Check.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : DB_Check.py
8 | # @Function : 数据库检测
9 | import os
10 | import time
11 | from sqlite3 import Cursor, Connection
12 |
13 | from inside.Config.Path import PATH
14 | from inside.DB.DB_Create import DB_CREATE
15 | from inside.DB.DB_Config import DB_CONFIG
16 | from inside.Template.Meta_Singleton import SINGLETON
17 |
18 | __all__ = ['DB_CHECK']
19 |
20 |
21 | class DB_CHECK(metaclass=SINGLETON):
22 | """数据库检测类"""
23 |
24 | @property
25 | def Dir(self) -> bool:
26 | """
27 | Dir -> bool
28 | 是否存在数据库目录
29 |
30 | :return: bool
31 | """
32 | return os.path.exists(PATH().DB)
33 |
34 | def Check_Dir(self) -> None:
35 | """
36 | Check_Dir() -> None
37 | 检查数据库目录是否存在,如不存在则自动创建
38 |
39 | :return: None
40 | """
41 | if not self.Dir:
42 | print(f"检测到数据库目录未创建\n"
43 | f"自动创建中")
44 | os.mkdir(PATH().DB)
45 | print(f"数据库目录为{PATH().DB}")
46 |
47 | def _Exist_Table(self, cursor: Cursor, table_name: str) -> bool:
48 | """
49 | _Exist_Table(cursor: Cursor, table_name: str) -> None
50 | 检查表是否存在
51 |
52 | :param cursor: 光标
53 | :param table_name: 表名
54 | :return: bool
55 | """
56 | sql = '''SELECT name FROM sqlite_master
57 | WHERE type='table'
58 | AND name=? COLLATE NOCASE;'''
59 | data = (table_name,)
60 | result = cursor.execute(sql, data)
61 | try:
62 | next(result)
63 | return True
64 | except StopIteration:
65 | return False
66 |
67 | def _Change_Table(self, connect: Connection, cursor: Cursor,
68 | table_name: str) -> None:
69 | """
70 | _Change_Table(connect: Connection, cursor: Cursor,
71 | table_name: str) -> None
72 | 更改表名,在表名后面加上当前时间戳
73 |
74 | :param connect: 数据库连接对象
75 | :param cursor: 光标
76 | :param table_name: 表名
77 | :return: None
78 | """
79 | new_name = table_name + str(int(time.time()))
80 | sql = f'''ALTER TABLE {table_name} RENAME TO {new_name};'''
81 | print(f"检测到同名不同结构表<{table_name}>\n"
82 | f"自动为其重命名中")
83 | cursor.execute(sql)
84 | connect.commit()
85 | print(f"重命名为<{new_name}>")
86 |
87 | def _Check_Table_Fields(self, cursor: Cursor, table_name: str,
88 | table_info: tuple) -> bool:
89 | """
90 | _Check_Table_Fields(cursor: Cursor, table_name: str,
91 | table_info: tuple) -> bool
92 | 检查表结构是否一致
93 |
94 | :param cursor: 光标
95 | :param table_name: 表名
96 | :param table_info: 表结构(从左至右顺序)
97 | :return: bool
98 | """
99 | sql = f'''PRAGMA TABLE_INFO({table_name})'''
100 | result = cursor.execute(sql)
101 | temp_name, temp_type = [], []
102 | for temp in result:
103 | temp_name.append(temp[1])
104 | temp_type.append(temp[2])
105 | return (tuple(temp_name), tuple(temp_type)) == table_info
106 |
107 | def _Check_Table(self, connect: Connection, cursor: Cursor,
108 | table_name: str, table_info: tuple) -> bool:
109 | """
110 | _Check_Table(connect: Connection, cursor: Cursor,
111 | table_name: str, table_info: tuple) -> bool:
112 | 检查表是否存在,且表结构是否一致;如存在且表结构不一致,则将已存在同名表重命名,返回False
113 |
114 | :param connect: 数据库连接对象
115 | :param cursor: 光标
116 | :param table_name: 表名
117 | :param table_info: 表结构(从左至右顺序)
118 | :return: bool
119 | """
120 | if self._Exist_Table(cursor=cursor, table_name=table_name):
121 | temp = self._Check_Table_Fields(cursor=cursor,
122 | table_name=table_name,
123 | table_info=table_info)
124 | if temp:
125 | return temp
126 | else:
127 | self._Change_Table(connect=connect, cursor=cursor,
128 | table_name=table_name)
129 | return False
130 |
131 | def Check_User(self, connect: Connection, db_create: DB_CREATE,
132 | cursor: Cursor) -> None:
133 | """
134 | Check_User(connect: Connection, db_create: DB_CREATE,
135 | cursor: Cursor) -> None
136 | 检查用户表是否存在,不存在则创建
137 |
138 | :param connect: 数据库连接对象
139 | :param db_create: 数据库创建操作对象
140 | :param cursor: 光标
141 | :return: None
142 | """
143 | if not self._Check_Table(
144 | connect=connect,
145 | cursor=cursor,
146 | table_name=DB_CONFIG().User,
147 | table_info=(
148 | DB_CONFIG().User_Fields,
149 | DB_CONFIG().User_Fields_Types
150 | )
151 | ):
152 | print(f"检测到用户表<{DB_CONFIG().User}>未创建\n"
153 | f"自动创建中")
154 | db_create.User()
155 | print(f"用户表为<{DB_CONFIG().User}>")
156 |
157 | def Check_Task(self, connect: Connection, db_create: DB_CREATE,
158 | cursor: Cursor) -> None:
159 | """
160 | Check_Task(connect: Connection, db_create: DB_CREATE,
161 | cursor: Cursor) -> None
162 | 检查任务表是否存在,不存在则创建
163 |
164 | :param connect: 数据库连接对象
165 | :param db_create: 数据库创建操作对象
166 | :param cursor: 光标
167 | :return: None
168 | """
169 | if not self._Check_Table(
170 | connect=connect,
171 | cursor=cursor,
172 | table_name=DB_CONFIG().Task,
173 | table_info=(
174 | DB_CONFIG().Task_Fields,
175 | DB_CONFIG().Task_Fields_Types
176 | )
177 | ):
178 | print(f"检测到任务表<{DB_CONFIG().Task}>未创建\n"
179 | f"自动创建中")
180 | db_create.Task()
181 | print(f"任务表为<{DB_CONFIG().Task}>")
182 |
183 | def Check_Article(self, connect: Connection, db_create: DB_CREATE,
184 | cursor: Cursor) -> None:
185 | """
186 | Check_Article(connect: Connection, db_create: DB_CREATE,
187 | cursor: Cursor) -> None
188 | 检查文章表是否存在,不存在则创建
189 |
190 | :param connect: 数据库连接对象
191 | :param db_create: 数据库创建操作对象
192 | :param cursor: 光标
193 | :return: None
194 | """
195 | if not self._Check_Table(
196 | connect=connect,
197 | cursor=cursor,
198 | table_name=DB_CONFIG().Article,
199 | table_info=(
200 | DB_CONFIG().Article_Fields,
201 | DB_CONFIG().Article_Fields_Types
202 | )
203 | ):
204 | print(f"检测到文章表<{DB_CONFIG().Article}>未创建\n"
205 | f"自动创建中")
206 | db_create.Article()
207 | print(f"文章表为<{DB_CONFIG().Article}>")
208 |
209 | def Check_Video(self, connect: Connection, db_create: DB_CREATE,
210 | cursor: Cursor) -> None:
211 | """
212 | Check_Video(connect: Connection, db_create: DB_CREATE,
213 | cursor: Cursor) -> None
214 | 检查视频表是否存在,不存在则创建
215 |
216 | :param connect: 数据库连接对象
217 | :param db_create: 数据库创建操作对象
218 | :param cursor: 光标
219 | :return: None
220 | """
221 | if not self._Check_Table(
222 | connect=connect,
223 | cursor=cursor,
224 | table_name=DB_CONFIG().Video,
225 | table_info=(
226 | DB_CONFIG().Video_Fields,
227 | DB_CONFIG().Video_Fields_Types
228 | )
229 | ):
230 | print(f"检测到视频表<{DB_CONFIG().Video}>未创建\n"
231 | f"自动创建中")
232 | db_create.Video()
233 | print(f"视频表为<{DB_CONFIG().Video}>")
234 |
235 | def Check_Project(self, connect: Connection, db_create: DB_CREATE,
236 | cursor: Cursor) -> None:
237 | """
238 | Check_Project(connect: Connection, db_create: DB_CREATE,
239 | cursor: Cursor) -> None
240 | 检查专项答题表是否存在,不存在则创建
241 |
242 | :param connect: 数据库连接对象
243 | :param db_create: 数据库创建操作对象
244 | :param cursor: 光标
245 | :return: None
246 | """
247 | if not self._Check_Table(
248 | connect=connect,
249 | cursor=cursor,
250 | table_name=DB_CONFIG().Project,
251 | table_info=(
252 | DB_CONFIG().Project_Fields,
253 | DB_CONFIG().Project_Fields_Types
254 | )
255 | ):
256 | print(f"检测到专项答题表<{DB_CONFIG().Project}>未创建\n"
257 | f"自动创建中")
258 | db_create.Project()
259 | print(f"视频表为<{DB_CONFIG().Project}>")
260 |
261 | def Check_Baidu_AI(self, connect: Connection, db_create: DB_CREATE,
262 | cursor: Cursor) -> None:
263 | """
264 | Check_Project(connect: Connection, db_create: DB_CREATE,
265 | cursor: Cursor) -> None
266 | 检查专项答题表是否存在,不存在则创建
267 |
268 | :param connect: 数据库连接对象
269 | :param db_create: 数据库创建操作对象
270 | :param cursor: 光标
271 | :return: None
272 | """
273 | if not self._Check_Table(
274 | connect=connect,
275 | cursor=cursor,
276 | table_name=DB_CONFIG().Baidu_AI,
277 | table_info=(
278 | DB_CONFIG().Baidu_AI_Fields,
279 | DB_CONFIG().Baidu_AI_Fields_Types
280 | )
281 | ):
282 | print(f"检测到百度AI表<{DB_CONFIG().Baidu_AI}>未创建\n"
283 | f"自动创建中")
284 | db_create.Baidu_AI()
285 | print(f"百度AI表为<{DB_CONFIG().Baidu_AI}>")
286 |
287 | def Check_Table(self, connect: Connection, cursor: Cursor) -> None:
288 | """
289 | Check_Table(connect: Connection, cursor: Cursor) -> None
290 | 检查表信息
291 |
292 | :param connect: 数据库连接对象
293 | :param cursor: 光标
294 | :return: None
295 | """
296 | db_create = DB_CREATE(connect=connect, cursor=cursor)
297 | self.Check_User(connect=connect, db_create=db_create, cursor=cursor)
298 | self.Check_Task(connect=connect, db_create=db_create, cursor=cursor)
299 | self.Check_Article(connect=connect, db_create=db_create, cursor=cursor)
300 | self.Check_Video(connect=connect, db_create=db_create, cursor=cursor)
301 | self.Check_Project(connect=connect, db_create=db_create, cursor=cursor)
302 | self.Check_Baidu_AI(connect=connect, db_create=db_create, cursor=cursor)
303 |
304 |
--------------------------------------------------------------------------------
/inside/Config/Api.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2020/12/13
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.2
7 | # @File : Api.py
8 | # @Function : api配置
9 | from typing import List
10 | from urllib.parse import ParseResult
11 | from urllib.parse import urlparse
12 |
13 | from inside.Driver.Driver_Analysis import GOOGLEAPIS_DRIVER_ANALYSIS, \
14 | TAOBAO_DRIVER_ANALYSIS
15 | from inside.Template.ABC_Driver_Analysis import DRIVER_ANALYSIS
16 | from inside.Template.Meta_Singleton import SINGLETON
17 |
18 | __all__ = ['API']
19 |
20 | from inside.Tools.Url_Test import URL_TEST
21 |
22 |
23 | class API(metaclass=SINGLETON):
24 | """Api集合类"""
25 |
26 | @property
27 | def Master(self) -> ParseResult:
28 | """
29 | Master -> ParseResult
30 | 官网
31 |
32 | :return: ParseResult
33 | """
34 | return urlparse(
35 | url="https://www.xuexi.cn/"
36 | )
37 |
38 | @property
39 | def Login(self) -> ParseResult:
40 | """
41 | Login -> ParseResult
42 | 登录api
43 |
44 | :return: ParseResult
45 | """
46 | return urlparse(
47 | url="https://pc.xuexi.cn/points/login.html?ref=https://www.xuexi"
48 | ".cn/"
49 | )
50 |
51 | @property
52 | def Login_Generate(self) -> ParseResult:
53 | """
54 | Login_Generate -> ParseResult
55 | 登录二维码号码api
56 |
57 | :return: ParseResult
58 | """
59 | return urlparse(
60 | url="https://login.xuexi.cn/user/qrcode/generate"
61 | )
62 |
63 | @property
64 | def Login_QR_Status(self) -> ParseResult:
65 | """
66 | Login_State -> ParseResult
67 | 登录二维码状态api
68 |
69 | :return: ParseResult
70 | """
71 | return urlparse(
72 | url="https://login.xuexi.cn/login/login_with_qr"
73 | )
74 |
75 | @property
76 | def Login_QR_Iframe(self) -> ParseResult:
77 | """
78 | Login_QR_Iframe -> ParseResult
79 | 登录二维码state的正则规则api
80 |
81 | :return: ParseResult
82 | """
83 | return urlparse(
84 | url="https://login.xuexi.cn/login/xuexiWeb\\?appid"
85 | "=dingoankubyrfkttorhpou&goto=https://oa.xuexi.cn&type=1"
86 | "&state=.*?&check_login=https://pc-api.xuexi.cn"
87 | )
88 |
89 | @property
90 | def Login_Token(self) -> ParseResult:
91 | """
92 | Login_Token -> ParseResult
93 | 登录cookie获取api
94 |
95 | :return: ParseResult
96 | """
97 | return urlparse(
98 | url="https://pc-api.xuexi.cn/login/secure_check?code={"
99 | "code}&state={state}"
100 | )
101 |
102 | @property
103 | def Token_Check(self) -> ParseResult:
104 | """
105 | Token_Check(self) -> ParseResult
106 | Token检测api
107 |
108 | :return: ParseResult
109 | """
110 | return urlparse(
111 | url="https://pc-api.xuexi.cn/open/api/auth/check?t={timestamp}"
112 | )
113 |
114 | @property
115 | def Aggregate_Score(self) -> ParseResult:
116 | """
117 | Aggregate_Score -> ParseResult
118 | 总积分api
119 |
120 | :return: ParseResult
121 | """
122 | return urlparse(
123 | url="https://pc-api.xuexi.cn/open/api/score/get?_t=1606620395029"
124 | )
125 |
126 | @property
127 | def Daily_Score(self) -> ParseResult:
128 | """
129 | Daily_Score -> ParseResult
130 | 每日已获积分api
131 |
132 | :return: ParseResult
133 | """
134 | return urlparse(
135 | url="https://pc-api.xuexi.cn/open/api/score/today/query"
136 | )
137 |
138 | @property
139 | def Task_Bar(self) -> ParseResult:
140 | """
141 | Task_Bar -> ParseResult
142 | 每日任务进度api
143 |
144 | :return: ParseResult
145 | """
146 | return urlparse(
147 | url="https://pc-api.xuexi.cn/open/api/score/today/queryrate"
148 | )
149 |
150 | @property
151 | def Level(self) -> ParseResult:
152 | """
153 | Level -> ParseResult
154 | 等级api
155 |
156 | :return: ParseResult
157 | """
158 | return urlparse(
159 | url="https://pc-api.xuexi.cn/open/api/score/self/get"
160 | )
161 |
162 | @property
163 | def Task_Parent(self) -> ParseResult:
164 | """
165 | Task_Parent -> ParseResult
166 | 所有任务类别api
167 |
168 | :return: ParseResult
169 | """
170 | return urlparse(
171 | url="https://www.xuexi.cn/lgdata/channel-list.json?_st=26777175"
172 | )
173 |
174 | @property
175 | def Task_Son(self) -> ParseResult:
176 | """
177 | Task_Son -> ParseResult
178 | 具体任务集合api,此处返回Url中需格式化后使用,格式变量为channel_id,此变量值从
179 | Task_Parent中解析而来;
180 | 使用方法:Task_Son.geturl().format(channel_id=xxx)
181 |
182 | :return: ParseResult
183 | """
184 | return urlparse(
185 | url="https://www.xuexi.cn/lgdata/{channel_id}.json?_st=26777175"
186 | )
187 |
188 | @property
189 | def Task_Check(self) -> ParseResult:
190 | return urlparse(
191 | url="https://iflow-api.xuexi.cn/logflow/api/v1/pclog"
192 | )
193 |
194 | @property
195 | def Not_Found(self) -> ParseResult:
196 | """
197 | Not_Found -> ParseResult
198 | 404api
199 |
200 | :return: ParseResult
201 | """
202 | return urlparse(
203 | url="https://www.xuexi.cn/notFound.html"
204 | )
205 |
206 | @property
207 | def Daily_Answer(self) -> ParseResult:
208 | """
209 | Daily_Answer -> ParseResult
210 | 每日答题api
211 |
212 | :return: ParseResult
213 | """
214 | return urlparse(
215 | url="https://pc.xuexi.cn/points/exam-practice.html"
216 | )
217 |
218 | @property
219 | def Daily_Answer_Res(self) -> ParseResult:
220 | """
221 | Daily_Answer_Res -> ParseResult
222 | 每日答题答案api
223 |
224 | :return: ParseResult
225 | """
226 | return urlparse(
227 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/common"
228 | "/deduplicateRandomSearchV3?limit=5&activityCode=QUIZ_ALL"
229 | "&forced=true"
230 | )
231 |
232 | @property
233 | def Daily_Answer_Put(self) -> ParseResult:
234 | """
235 | Daily_Answer_Put -> ParseResult
236 | 每日答题答案提交api
237 |
238 | :return: ParseResult
239 | """
240 | return urlparse(
241 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/practice"
242 | "/quizSubmitV3"
243 | )
244 |
245 | @property
246 | def Answer_Accomplish(self) -> ParseResult:
247 | """
248 | Answer_Accomplish -> ParseResult
249 | 答题完成api
250 |
251 | :return: ParseResult
252 | """
253 | return urlparse(
254 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/detail/score"
255 | "\\?queryTipScoreId=.*"
256 | )
257 |
258 | @property
259 | def Weekly_Answer_Topics(self) -> ParseResult:
260 | """
261 | Weekly_Answer_Topics -> ParseResult
262 | 每周答题总览api,由于每周答题2021/01/10改版,所有答题不过期,所以将获取往期未答;
263 | 而目前总共为3页,所以num从3开始,其实可以调整pageSize的大小,一次性将题目列表
264 | 全部获取,但不是正常的操作。
265 |
266 | :return: ParseResult
267 | """
268 | return urlparse(
269 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/practice/pc"
270 | "/weekly/more?pageSize=50&pageNo={num}"
271 | )
272 |
273 | @property
274 | def Weekly_Answer_Topic(self) -> ParseResult:
275 | """
276 | Weekly_Answer_Topic -> ParseResult
277 | 每周答题api,此处返回Url中需格式化后使用,格式变量为num,此变量值从
278 | Weekly_Answer_Topics中解析而来。
279 | 使用方法:Weekly_Answer_Topic.geturl().format(num=x)
280 |
281 | :return: ParseResult
282 | """
283 | return urlparse(
284 | url="https://pc.xuexi.cn/points/exam-weekly-detail.html?id={num}"
285 | )
286 |
287 | @property
288 | def Weekly_Answer_Res(self) -> ParseResult:
289 | """
290 | Weekly_Answer_Res -> ParseResult
291 | 每周答题答案api,此处返回Url中需格式化后使用,格式变量为num,此变量值从
292 | Weekly_Answer_Topics中解析而来并与Weekly_Answer_Topic统一;
293 | 使用方法:Weekly_Answer_Res.geturl().format(num=x)
294 |
295 | :return: ParseResult
296 | """
297 | return urlparse(
298 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/detail"
299 | "/queryV3?type=2&id={num}&forced=true"
300 | )
301 |
302 | @property
303 | def Project_Answer_Topics(self) -> ParseResult:
304 | """
305 | Project_Answer_Topics -> ParseResult
306 | 专项答题总览api,由于每周答题2021/01/10改版,所有答题不过期,所以将获取往期未答;
307 | 而目前总共为5页,所以num从5开始,其实可以调整pageSize的大小,一次性将题目列表
308 | 全部获取,但不是正常的操作。
309 |
310 | :return: ParseResult
311 | """
312 | return urlparse(
313 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/paper/pc"
314 | "/list?pageSize=50&pageNo={num}"
315 | )
316 |
317 | @property
318 | def Project_Answer_Topic(self) -> ParseResult:
319 | """
320 | Project_Answer_Topic -> ParseResult
321 | 专项答题api,此处返回Url中需格式化后使用,格式变量为page,此变量值从
322 | Project_Answer_Topics解析而来。
323 | 使用方法:Project_Answer_Topic.geturl().format(num=x)
324 |
325 | :return: ParseResult
326 | """
327 | return urlparse(
328 | url="https://pc.xuexi.cn/points/exam-paper-detail.html?id={num}"
329 | )
330 |
331 | @property
332 | def Project_Answer_Res(self) -> ParseResult:
333 | """
334 | Project_Answer_Res -> ParseResult
335 | 专项答题答案api,此处返回Url中需格式化后使用,格式变量为num,此变量值从
336 | Project_Answer_Topics中解析而来并与Project_Answer_Topic统一;
337 | 使用方法:Project_Answer_Res.geturl().format(num=x)
338 |
339 | :return: ParseResult
340 | """
341 | return urlparse(
342 | url="https://pc-proxy-api.xuexi.cn/api/exam/service/detail"
343 | "/queryV3?type=1&id={num}&forced=true"
344 | )
345 |
346 | @property
347 | def __Drivers(self) -> List[DRIVER_ANALYSIS]:
348 | """
349 | __Drivers -> List[ParseResult]
350 | 驱动下载站点集合
351 |
352 | :return: List[ParseResult]
353 | """
354 | google = GOOGLEAPIS_DRIVER_ANALYSIS()
355 | taobao = TAOBAO_DRIVER_ANALYSIS()
356 | return [google, taobao]
357 |
358 | @property
359 | def Driver(self) -> DRIVER_ANALYSIS:
360 | """
361 | Driver -> DRIVER_ANALYSIS
362 | 测试所有驱动下载站点,返回最佳站点
363 |
364 | :return: DRIVER_ANALYSIS
365 | """
366 | temp = float('inf')
367 | driver = None
368 | for value in self.__Drivers:
369 | test = URL_TEST.Url_Test(url=value.Master.geturl())
370 | if test and test < temp:
371 | temp = test
372 | driver = value
373 | return driver
374 |
--------------------------------------------------------------------------------
/inside/Login/Login.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # @Author : lisztomania
4 | # @Date : 2021/1/15
5 | # @Software : Pycharm
6 | # @Version : Python 3.8.5
7 | # @File : Login.py
8 | # @Function : 登录
9 | import json
10 | import re
11 | from typing import Dict
12 | from urllib import parse
13 |
14 | from selenium.common.exceptions import WebDriverException
15 | from selenium.webdriver.chrome.webdriver import WebDriver
16 | from selenium.webdriver.remote.webelement import WebElement
17 | from selenium.webdriver.support.wait import WebDriverWait
18 | from selenium.webdriver.support import expected_conditions as EC
19 | from selenium.webdriver.common.by import By
20 | from inside.Config.Api import API
21 | from inside.Options.Options import OPTIONS
22 | from inside.DB.DB_Manage import DB_MANAGE
23 | from inside.DB.Table_Class.User import USER
24 | from inside.Info.Info_Manage import INFO_MANAGE
25 | from inside.Login.Check_Token import CHECK_TOKEN
26 | from inside.Login.QR_Vessel import QR_VESSEL
27 |
28 | from inside.Tools.Network import NETWORK
29 | from inside.Tools.Requests import REQUESTS
30 |
31 | __all__ = ['LOGIN']
32 |
33 |
34 | class LOGIN(object):
35 | """登录类"""
36 |
37 | def __init__(self, task_driver: WebDriver):
38 | """
39 | LOGIN()
40 | 初始化,
41 |
42 | """
43 | self.__driver = task_driver
44 | self.__network = NETWORK()
45 | self.__wait = WebDriverWait(self.__driver, 10)
46 |
47 | def __QR_Iframe(self) -> WebElement:
48 | """
49 | __QR_Iframe() -> WebElement
50 | 获取二维码iframe
51 |
52 | :return: WebElement
53 | """
54 | iframe_Ec = EC.presence_of_element_located(
55 | (
56 | By.ID, "ddlogin-iframe"
57 | )
58 | )
59 | return self.__wait.until(iframe_Ec)
60 |
61 | def __QR_Base64(self) -> str:
62 | """
63 | __QR_Base64() -> str
64 | 二维码base64,格式为:data:image/png;base64,xxx==
65 |
66 | :return: str
67 | """
68 | qr_code_Ec = EC.presence_of_element_located(
69 | (By.CLASS_NAME, "login_qrcode_content")
70 | )
71 | qr_code: WebElement = self.__wait.until(qr_code_Ec)
72 | qr = qr_code.find_element_by_tag_name(name='img')
73 | return qr.get_attribute(name='src')
74 |
75 | def __Get_QR(self) -> str:
76 | """
77 | __Get_QR() -> str
78 | 获取二维码,格式为:data:image/png;base64,xxx==
79 |
80 | :return: str
81 | """
82 | qr_iframe = self.__QR_Iframe()
83 | self.__driver.switch_to.frame(frame_reference=qr_iframe)
84 | temp = self.__QR_Base64()
85 | self.__driver.switch_to.default_content()
86 | return temp
87 |
88 | def __Get_QR_ID(self, key: str, value: str) -> str:
89 | """
90 | __Get_QR_ID(key: str, value: str) -> str
91 | 获取二维码ID,格式为:qr:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
92 |
93 | :param key: url
94 | :param value: requestId
95 | :return: str
96 | """
97 | if API().Login_Generate.geturl() == key:
98 | response = self.__network.GetResponseBody(requestId=value)[
99 | 'body']
100 | response = json.loads(response)
101 | return response['result']
102 | return ''
103 |
104 | def __Get_QR_Status_Network(self, key: str, value: str) -> int:
105 | """
106 | __Get_QR_Status_Network(key: str, value: str) -> int
107 | 获取二维码状态码(Network中获取)
108 |
109 | :param key: url
110 | :param value: requestId
111 | :return: int
112 | -1: 还未请求
113 | 1: 成功
114 | 11021: 未登录
115 | 11019: 二维码失效
116 | """
117 | if API().Login_QR_Status.geturl() == key:
118 | while True:
119 | try:
120 | response = self.__network.GetResponseBody(requestId=value)
121 | response = json.loads(response['body'])
122 | if response['success']:
123 | return 1
124 | else:
125 | return int(response['code'])
126 | except WebDriverException:
127 | continue
128 | return -1
129 |
130 | def __Get_QR_Status_Requests(self, qr_data: Dict, qr_state: str):
131 | """
132 | __Get_QR_Status_Requests(qr_data: Dict) -> int
133 | 获取二维码状态码(requests请求)
134 |
135 | :param qr_data: 带有二维码编号的data
136 | :param qr_state: 二维码的state
137 | :return: int
138 | 1: 成功
139 | 11021: 未登录
140 | 11019: 二维码失效
141 | """
142 | html = REQUESTS.Post(
143 | url=API().Login_QR_Status.geturl(),
144 | data=qr_data
145 | )
146 | status = html.json()
147 | if status['success']:
148 | self.__Change_Driver_Cookie(key=status['data'], state=qr_state)
149 | return 1
150 | else:
151 | return int(status['code'])
152 |
153 | def __Change_Driver_Cookie(self, key: str, state: str) -> None:
154 | """
155 | __Change_Driver_Cookie(key: str, state: str) -> None
156 | 此处是应对network中还未查询二维码状态时,所做的措施,用处是加快登录过程,而不需要浏览器
157 | 自主添加cookie
158 |
159 | :param key: 最后一次查询二维码状态的api会返回code(只会允许查询一次,随后就是过期)
160 | :param state: iframe链接中提取
161 | :return: None
162 | """
163 | url = parse.urlparse(url=key)
164 | code = parse.parse_qs(qs=url.query)['loginTmpCode'][0]
165 | html = REQUESTS.Get(
166 | url=API().Login_Token.geturl().format(code=code, state=state)
167 | )
168 | token = html.cookies.get(name='token')
169 | cookie = {'domain': '.xuexi.cn',
170 | 'name': 'token',
171 | 'value': token}
172 | self.__driver.add_cookie(cookie_dict=cookie)
173 | self.__driver.refresh()
174 |
175 | def __Get_QR_State(self, key: str) -> str:
176 | """
177 | __Get_QR_State(key: str) -> str
178 | 获取二维码的state,在iframe链接中提取
179 |
180 | :param key: iframe链接
181 | :return: str
182 | """
183 | temp = re.match(
184 | pattern=API().Login_QR_Iframe.geturl(),
185 | string=key
186 | )
187 | if temp:
188 | url = parse.urlparse(url=key)
189 | return parse.parse_qs(qs=url.query)['state'][0]
190 | return ''
191 |
192 | def __QR_Refresh(self) -> None:
193 | """
194 | __QR_Refresh() -> None
195 | 刷新二维码
196 |
197 | :return: None
198 | """
199 | qr_refresh_Ec = EC.presence_of_element_located(
200 | (
201 | By.CLASS_NAME, "refresh"
202 | )
203 | )
204 | qr_refresh: WebElement = self.__wait.until(qr_refresh_Ec)
205 | qr_refresh.click()
206 |
207 | def __Check_Status(self) -> bool:
208 | """
209 | __Check_Status() -> bool
210 | 检测二维码状态, 此处略微复杂了一点
211 | 具体流程:
212 | 1、初始化state、iid、status、req为空
213 | state:二维码的state值,用于处理浏览器未触发状态检测的空窗期登录问题
214 | iid:二维码的id值,作用同上
215 | status:二维码的状态值
216 | req:检测浏览器是否处于空窗期
217 | 2、开启循环
218 | 3、从日志中取值
219 | 4、为state、iid、status、req赋值
220 | 5、如果日志中没有了、且初值都赋值完毕,且浏览器处于空窗期,就进行自主查询
221 | 6、判断查询的状态
222 |
223 | :return: bool
224 | True:成功
225 | False:过期
226 | """
227 | state = iid = status = None
228 | req = False
229 | while True:
230 | network = self.__network.Get()
231 | for key, value in network.items():
232 | key = parse.unquote(string=key)
233 | temp = self.__Get_QR_State(key=key)
234 | if temp:
235 | state = temp
236 | break
237 | temp = self.__Get_QR_ID(key=key, value=value)
238 | if temp:
239 | iid = temp
240 | break
241 | status = self.__Get_QR_Status_Network(key=key, value=value)
242 | if status == 11019:
243 | return False
244 | req = True if status == -1 else False
245 | if not network and state and iid and status and req:
246 | qr_data = {
247 | 'qrCode': iid,
248 | 'goto': 'https://oa.xuexi.cn',
249 | 'pdmToken': ''
250 | }
251 | status = self.__Get_QR_Status_Requests(
252 | qr_data=qr_data,
253 | qr_state=state
254 | )
255 | if status == 1:
256 | return True
257 | elif status == 11021:
258 | continue
259 |
260 | def _Login(self) -> bool:
261 | """
262 | _Login() -> Bool
263 | 进行登录;
264 | 具体流程:
265 | 1、开启循环检测二维码状态
266 | 1、获取二维码图片
267 | 2、显示二维码
268 | 3、二维码状态检测
269 | 4、根据3的返回值决定:
270 | 1、刷新二维码,中断本次循环,再来一次
271 | 2、提取Token值,根据选项(持久化)决定是否保持token,关闭二维码容器
272 | :return: Bool,返回值只有True,如未登录则会一直循环
273 | """
274 | self.__network.Clear()
275 | self.__driver.get(url=API().Login.geturl())
276 | while True:
277 | qr = self.__Get_QR()
278 | QR_VESSEL().Show_QR(qr=qr)
279 | status = self.__Check_Status()
280 | if not status:
281 | self.__QR_Refresh()
282 | continue
283 | else:
284 | while self.__driver.current_url != API().Master.geturl():
285 | continue
286 | cookies = self.__driver.get_cookies()
287 | token = [{cookie['name']: cookie['value']} for cookie in
288 | cookies if cookie['name'] == 'token']
289 | if token:
290 | INFO_MANAGE().Init(token=token[0]['token'])
291 | if OPTIONS().Token:
292 | cookie = token[0]
293 | html = REQUESTS().Get(
294 | url=API().Aggregate_Score.geturl(),
295 | cookies=cookie
296 | )
297 | data = html.json()
298 | user_id = data['data']['userId']
299 | user = USER(user_id=user_id, token=token[0]['token'])
300 | if DB_MANAGE().User.Exist_User(user=user):
301 | DB_MANAGE().User.Update(user=user)
302 | else:
303 | DB_MANAGE().User.Insert(user=user)
304 | DB_MANAGE().Article.Update_All()
305 | DB_MANAGE().Video.Update_All()
306 | QR_VESSEL().QR_QUIT()
307 | return status
308 |
309 | def Login(self) -> bool:
310 | """
311 | Login() -> bool
312 | 登录,如果user表不为空会检测令牌是否有效,有效则直接登录
313 |
314 | :return: bool
315 | """
316 | if DB_MANAGE().User.Exist():
317 | user = DB_MANAGE().User.Query()
318 | if CHECK_TOKEN.Check_Token(token=user.Token):
319 | INFO_MANAGE().Init(token=user.Token)
320 | self.__driver.get(url=API().Master.geturl())
321 | self.__driver.add_cookie(cookie_dict=user.Token_Driver)
322 | self.__driver.refresh()
323 | return True
324 | return self._Login()
325 |
326 |
--------------------------------------------------------------------------------