├── .idea
├── .gitignore
├── vcs.xml
├── inspectionProfiles
│ ├── profiles_settings.xml
│ └── Project_Default.xml
├── modules.xml
└── juliangip_dailycheckin.iml
├── requirements.txt
├── config.json
├── README.md
├── set_proxy_pool.py
├── tencentcaptcha.py
└── juliang.py
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # 默认忽略的文件
2 | /shelf/
3 | /workspace.xml
4 | # 基于编辑器的 HTTP 客户端请求
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | attrs==23.2.0
2 | certifi==2024.2.2
3 | cffi==1.16.0
4 | charset-normalizer==3.3.2
5 | exceptiongroup==1.2.1
6 | h11==0.14.0
7 | idna==3.7
8 | outcome==1.3.0.post0
9 | pycparser==2.22
10 | pyperclip==1.8.2
11 | PySocks==1.7.1
12 | requests==2.32.2
13 | selenium==4.21.0
14 | sniffio==1.3.1
15 | sortedcontainers==2.4.0
16 | trio==0.25.1
17 | trio-websocket==0.11.1
18 | typing_extensions==4.12.0
19 | urllib3==2.2.1
20 | wsproto==1.2.0
21 |
--------------------------------------------------------------------------------
/.idea/juliangip_dailycheckin.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "juliang_account": [
3 | "phonenumber@passwd@wxpusherUID"
4 | ],
5 | "ttshitu_config": {
6 | "username": "xxxx",
7 | "password": "xxxx"
8 | },
9 | "proxy_config": {
10 | "proxy_address": "",
11 | "proxy_username": "",
12 | "proxy_password": ""
13 | },
14 | "wxpush_config": {
15 | "appToken": "AT_xxxxxx",
16 | "title": "巨量IP签到"
17 | },
18 | "juliang_api_config": ["trade_no@API_KEY"
19 | ],
20 | "auto_proxy_pool_config": {
21 | "proxy_pool_url": "",
22 | "auth": {
23 | "username": "",
24 | "password": ""
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 巨量IP签到
2 |
3 | ## 介绍
4 | windows下的巨量IP签到脚本
5 |
6 | 滑块验证使用`Ttshitu`API接口
7 |
8 | ## 依赖
9 | ```
10 | python -m venv env
11 | env\Scripts\activate
12 | pip install -r requirements.txt
13 | ```
14 | ## 使用
15 | 打开`config.json`配置文件编辑:
16 | ```json
17 | {
18 | "juliang_account": [
19 | "phonenumber@passwd@wxpusherUID"
20 | ],
21 | "ttshitu_config": {
22 | "username": "xxxx",
23 | "password": "xxxx"
24 | },
25 | "proxy_config": {
26 | "proxy_address": "", // ip:port
27 | "proxy_username": "",
28 | "proxy_password": ""
29 | },
30 | "wxpush_config": {
31 | "appToken": "xxxxxx",
32 | "title": "巨量IP签到"
33 | },
34 | "juliang_api_config": ["trade_no@API_KEY"
35 | ],
36 | "auto_proxy_pool_config": {
37 | "proxy_pool_url": "", // http://ip:port
38 | "auth": {
39 | "username": "",
40 | "password": ""
41 | }
42 | }
43 | }
44 | ```
45 | - `juliang_account`:巨量账号。`wxpusherUID`:wxpusher推送的UID,可以不填。
46 | - `ttshitu_config`:图鉴API配置,**必填**。
47 | - `proxy_config`:代理配置,可以不填。
48 | - `wxpush_config`:wxpusher推送配置,可以不填。
49 | - `juliang_api_config`:巨量API,`trade_no`为业务编号,`API_KEY`为API密钥。
50 | - `auto_proxy_pool_config`:用于自动提交PROXY API到代理池,可以不填。
51 |
52 | 编辑`juliang.py`文件,修改第344行:
53 | ```python
54 | # 修改为你的chromedriver路径
55 | chromedriver_path = r'C:\Users\windowsuser\AppData\Local\Google\Chrome\Application\chromedriver.exe'
56 | ```
57 |
58 | **运行脚本**:
59 |
60 | `python juliang.py`
61 |
62 | 添加到计划任务:
63 |
64 | 项目文件夹下新建`*.bat`文件,编辑:
65 | ```bat
66 | @echo off
67 | set VIRTUAL_ENV=your_path\juliang_env
68 | set PATH=%VIRTUAL_ENV%\Scripts;%PATH%
69 | call %VIRTUAL_ENV%\Scripts\activate
70 | python your_path\juliangip_dailycheckin\juliang.py
71 | call %VIRTUAL_ENV%\Scripts\deactivate
72 | ```
73 | 将`*.bat`添加到计划任务。
74 |
75 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/set_proxy_pool.py:
--------------------------------------------------------------------------------
1 | import requests
2 | from requests.auth import HTTPBasicAuth
3 |
4 |
5 | def set_proxy_pool(proxy_pool_url, api_url_list, auth):
6 | # auto-proxy-pool配置
7 | data = {
8 | "log": {},
9 | "config": {
10 | "disableStatusView": "true",
11 | "httpUseProxy": "false"
12 | },
13 | "users": {},
14 | "upstream": {},
15 | "changeRequest": [],
16 | "dev": {},
17 | "DefaultFailBack": ""
18 | }
19 |
20 | # 动态生成 upstream 配置
21 | proxy_names = []
22 | for index, api_url in enumerate(api_url_list):
23 | proxy_name = f"proxy{index + 1}"
24 | proxy_names.append(proxy_name)
25 | data["upstream"][proxy_name] = {
26 | "apiUrl": api_url,
27 | "upstreamFixedAuth": "",
28 | "requestInterval": "1000ms",
29 | "lifecycle": "50s",
30 | "proactive": "20s",
31 | "proactiveOnIdleSize": 0,
32 | "proactiveOnIdleCheckInterval": "",
33 | "maxSize": 3,
34 | "loadBalanceMultiple": 1,
35 | "disableOptimizationIp": False,
36 | "enableReduceReuseIp": False,
37 | "groupIndex": 0,
38 | "failSleep": "",
39 | "failThreshold": 0,
40 | "disableHttpUseTunnel": False
41 | }
42 |
43 | # 将所有上游以逗号分隔的形式添加到 changeRequest
44 | data["changeRequest"].append({
45 | "hostRegex": ".*",
46 | "loadBalanceInterval": 0,
47 | "black": False,
48 | "proxy": ",".join(proxy_names)
49 | })
50 |
51 | # 认证凭证
52 | auth = HTTPBasicAuth(auth['username'], auth['password'])
53 |
54 | config_url = proxy_pool_url + '/admin/config'
55 |
56 | # 发送PUT请求
57 | response = requests.put(config_url, auth=auth, json=data)
58 |
59 | if response.status_code == 200:
60 | print(response.content.decode('utf-8'))
61 | return True
62 | else:
63 | print(f"失败,状态码: {response.status_code}")
64 | return False
65 |
66 |
67 | if __name__ == '__main__':
68 | # content = set_proxy_pool('http://ip:prot', 'http://v2.api.juliangip.com/dynamic/getips?num=1&pt=1&result_type=text&split=2&trade_no=1xxxxxxxxxxx1&sign=exxxxxxxxxxxxxxxxx0', 'user', 'passwd')
69 | # if content:
70 | # print("设置成功")
71 | pass
72 |
--------------------------------------------------------------------------------
/tencentcaptcha.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | @author:Pineapple
4 |
5 | @contact:cppjavapython@foxmail.com
6 |
7 | @time:2020/8/2 17:29
8 |
9 | @file:tencentcaptcha.py
10 |
11 | @desc: login with Tencen .
12 | """
13 | import base64
14 | import random
15 | import time
16 |
17 | import re
18 | import requests
19 | import os
20 |
21 | from selenium import webdriver
22 | from selenium.webdriver import ActionChains
23 | from selenium.webdriver.common.by import By
24 | from selenium.webdriver.support import expected_conditions as EC # 显性等待
25 | from selenium.webdriver.support.wait import WebDriverWait
26 |
27 | class Tencent:
28 | """
29 | 识别腾讯验证码
30 | """
31 |
32 | def __init__(self, ttshitu_username, ttshitu_password, browser=None):
33 | """
34 | 初始化浏览器配置,声明变量
35 |
36 | :param browser: 浏览器实例
37 | """
38 | self.browser = browser if browser else webdriver.Chrome()
39 | self.wait = WebDriverWait(self.browser, 20)
40 | self.ttshitu_username = ttshitu_username
41 | self.ttshitu_password = ttshitu_password
42 |
43 | def end(self):
44 | """
45 | 结束后退出,可选
46 | """
47 | if self.browser:
48 | self.browser.quit()
49 |
50 | def set_info(self):
51 | """
52 | 填写个人信息,在子类中完成
53 | """
54 | pass
55 |
56 | def tx_code(self):
57 | """
58 | 主要部分,函数入口
59 | """
60 | self.set_info()
61 | self.wait.until(EC.presence_of_element_located((By.ID, 'tcaptcha_transform_dy'))) # 等待 iframe
62 | self.browser.switch_to.frame(self.browser.find_element(By.ID, 'tcaptcha_iframe_dy')) # 加载 iframe
63 | time.sleep(2)
64 | style = self.browser.find_element(By.ID, 'slideBg').get_attribute("style")
65 | match = re.search('background-image: url\("(.*?)"\);', style)
66 | try:
67 | bk_block = match.group(1)
68 | if Tencent.save_img(bk_block):
69 | dex = self.get_pos()
70 | if dex:
71 | track_list = Tencent.get_track(dex)
72 | time.sleep(0.5)
73 | slid_ing = self.browser.find_element(By.XPATH, '//*[@id="tcOperation"]/div[6]') # 滑块定位
74 | ActionChains(self.browser).click_and_hold(on_element=slid_ing).perform() # 鼠标按下
75 | time.sleep(0.2)
76 | for track in track_list:
77 | ActionChains(self.browser).move_by_offset(xoffset=track, yoffset=0).perform() # 鼠标移动到距离当前位置(x,y)
78 | time.sleep(1)
79 | ActionChains(self.browser).release(on_element=slid_ing).perform() # print('第三步,释放鼠标')
80 | time.sleep(5)
81 | return True
82 | else:
83 | self.re_start()
84 | else:
85 | print('缺口图片捕获失败')
86 | return False
87 | except Exception as e:
88 | print('错误:', e.args)
89 | return False
90 |
91 | @staticmethod
92 | def save_img(bk_block):
93 | """
94 | 保存图片
95 |
96 | :param bk_block: 图片url
97 | :return: bool类型,是否被保存
98 | """
99 | try:
100 | img = requests.get(bk_block).content
101 | with open("bg.jpeg", 'wb') as f:
102 | f.write(img)
103 | return True
104 | except requests.exceptions.RequestException as e:
105 | print(f"获取图片时出错: {e}")
106 | return False
107 | except Exception as e:
108 | print(f"保存图像时出错: {e}")
109 | return False
110 |
111 | def get_pos(self):
112 | """
113 | 识别缺口
114 | 注意:网页上显示的图片为缩放图片,缩放 50% 所以识别坐标需要 0.5
115 |
116 | :return: 缺口位置
117 | """
118 | # image = cv.imread('bg.jpeg')
119 | # # 高斯滤波
120 | # blurred = cv.GaussianBlur(image, (5, 5), 0)
121 | # # 边缘检测
122 | # canny = cv.Canny(blurred, 200, 400)
123 | # # 轮廓检测
124 | # contours, hierarchy = cv.findContours(
125 | # canny, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
126 | # for i, contour in enumerate(contours):
127 | # m = cv.moments(contour)
128 | # if m['m00'] == 0:
129 | # cx = cy = 0
130 | # else:
131 | # cx, cy = m['m10'] / m['m00'], m['m01'] / m['m00']
132 | # if 6000 < cv.contourArea(contour) < 8000 and 370 < cv.arcLength(contour, True) < 390:
133 | # if cx < 400:
134 | # continue
135 | # x, y, w, h = cv.boundingRect(contour) # 外接矩形
136 | # cv.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
137 | # # cv.imshow('image', image) # 显示识别结果
138 | # print('【缺口识别】 {x}px'.format(x=x / 2))
139 | # return x / 2
140 | # return 0
141 | # 判断当前环境是windows还是linux
142 | imul = ''
143 | if os.name == 'nt':
144 | imul = 'bg.jpeg'
145 | elif os.name == 'posix':
146 | imul = '/ql/scripts/root_littlepythonsheep/bg.jpeg'
147 | with open(imul, 'rb') as f:
148 | base64_data = base64.b64encode(f.read())
149 | b64 = base64_data.decode()
150 |
151 | # 调用验证码识别API
152 | data = {
153 | "username": self.ttshitu_username,
154 | "password": self.ttshitu_password,
155 | "typeid": 33,
156 | "image": b64
157 | }
158 | try:
159 | response = requests.post("http://api.ttshitu.com/predict", json=data)
160 | response.raise_for_status() # 检查请求是否成功
161 | result = response.json()
162 | except requests.RequestException as e:
163 | print(f"请求失败: {e}")
164 | return 0
165 |
166 | # 解析识别结果
167 | try:
168 | dex = result["data"]["result"]
169 | dex = int(dex)
170 | dex -= 60
171 | print('【缺口识别】 {x}px'.format(x=dex / 2))
172 | return dex / 2
173 | except (KeyError, ValueError) as e:
174 | print(f"响应分析错误:{e}")
175 | return 0
176 |
177 | @staticmethod
178 | def get_track(distance):
179 | """
180 | 轨迹方程
181 |
182 | :param distance: 距缺口的距离
183 | :return: 位移列表
184 | """
185 | # distance -= 75 # 初始位置
186 | # 初速度
187 | v = 0
188 | # 单位时间为0.2s来统计轨迹,轨迹即0.2内的位移
189 | t = 0.2
190 | # 位移/轨迹列表,列表内的一个元素代表0.2s的位移
191 | tracks = []
192 | # 当前的位移
193 | current = 0
194 | # 到达mid值开始减速
195 | mid = distance * 4 / 5
196 |
197 | distance += 10 # 先滑过一点,最后再反着滑动回来
198 | # a = random.randint(1,3)
199 | while current < distance:
200 | if current < mid:
201 | # 加速度越小,单位时间的位移越小,模拟的轨迹就越多越详细
202 | # a = random.randint(2, 4) # 加速运动
203 | a = 3
204 | else:
205 | # a = -random.randint(3, 5) # 减速运动
206 | a = -2
207 | # 初速度
208 | v0 = v
209 | # 0.2秒时间内的位移
210 | s = v0 * t + 0.5 * a * (t ** 2)
211 | # 当前的位置
212 | current += s
213 | # 添加到轨迹列表
214 | tracks.append(round(s))
215 |
216 | # 速度已经达到v,该速度作为下次的初速度
217 | v = v0 + a * t
218 |
219 | # 反着滑动到大概准确位置
220 | for i in range(4):
221 | tracks.append(-random.randint(2, 3))
222 | for i in range(4):
223 | tracks.append(-random.randint(1, 3))
224 | return tracks
225 |
226 | def move_to(self, index):
227 | """
228 | 移动滑块
229 |
230 | :param index:
231 | :return:
232 | """
233 | pass
234 |
235 | def re_start(self):
236 | """
237 | 准备开始
238 |
239 | :return: None
240 | """
241 | self.tx_code()
242 | # self.end()
--------------------------------------------------------------------------------
/juliang.py:
--------------------------------------------------------------------------------
1 | from selenium import webdriver
2 | from selenium.webdriver.common.by import By
3 | from selenium.webdriver.support.ui import WebDriverWait
4 | from selenium.webdriver.chrome.service import Service
5 | from selenium.webdriver.chrome.options import Options
6 | from selenium.webdriver.support import expected_conditions as EC
7 | from tencentcaptcha import Tencent
8 | from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
9 | from set_proxy_pool import set_proxy_pool
10 | import urllib.parse
11 | import time
12 | import json
13 | import os
14 | import zipfile
15 | import requests
16 | import hashlib
17 |
18 |
19 | # 创建代理插件
20 | def create_proxy_extension(proxy_host, proxy_port, proxy_user, proxy_pass):
21 | manifest_json = """
22 | {
23 | "version": "1.0.0",
24 | "manifest_version": 2,
25 | "name": "Chrome Proxy",
26 | "permissions": [
27 | "proxy",
28 | "tabs",
29 | "unlimitedStorage",
30 | "storage",
31 | "",
32 | "webRequest",
33 | "webRequestBlocking"
34 | ],
35 | "background": {
36 | "scripts": ["background.js"]
37 | }
38 | }
39 | """
40 |
41 | background_js = f"""
42 | var config = {{
43 | mode: "fixed_servers",
44 | rules: {{
45 | singleProxy: {{
46 | scheme: "http",
47 | host: "{proxy_host}",
48 | port: parseInt({proxy_port})
49 | }},
50 | bypassList: ["localhost"]
51 | }}
52 | }};
53 | chrome.proxy.settings.set({{value: config, scope: "regular"}}, function() {{}});
54 | function callbackFn(details) {{
55 | return {{
56 | authCredentials: {{
57 | username: "{proxy_user}",
58 | password: "{proxy_pass}"
59 | }}
60 | }};
61 | }}
62 | chrome.webRequest.onAuthRequired.addListener(
63 | callbackFn,
64 | {{urls: [""]}},
65 | ['blocking']
66 | );
67 | """
68 |
69 | pluginfile = 'proxy_auth_plugin.zip'
70 |
71 | with zipfile.ZipFile(pluginfile, 'w') as zp:
72 | zp.writestr("manifest.json", manifest_json)
73 | zp.writestr("background.js", background_js)
74 |
75 | return pluginfile
76 |
77 |
78 | # 读取配置文件
79 | def read_config(file_path):
80 | try:
81 | abs_path = os.path.abspath(file_path)
82 | print(f"加载配置:{abs_path}")
83 | with open(file_path, 'r', encoding='utf-8') as file:
84 | config = json.load(file)
85 | return config
86 | except FileNotFoundError:
87 | print(f"配置文件 {file_path} 找不到。")
88 | except json.JSONDecodeError:
89 | print(f"从文件 {file_path} 解码JSON时出错。")
90 | return None
91 |
92 |
93 | # 获取配置信息
94 | def get_config(config, key, default=None):
95 | value = config.get(key, default)
96 | if value is None:
97 | raise ValueError(f"没有配置 {key}")
98 | return value
99 |
100 |
101 | # 检查代理配置是否有效
102 | def is_config_valid(config, keys):
103 | for key in keys:
104 | if key not in config or not config[key]:
105 | return False
106 | return True
107 |
108 |
109 | # wxpusher消息推送
110 | def wxpush(title, text, uids, appToken):
111 | content = f''' {title} {text}
'''
112 | urlpust = 'http://wxpusher.zjiecode.com/api/send/message'
113 | for uid in uids:
114 | datapust = {
115 | "appToken": appToken,
116 | "content": content,
117 | "summary": title,
118 | "contentType": 2,
119 | "uids": [uid]
120 | }
121 | try:
122 | response = requests.post(url=urlpust, json=datapust)
123 | if response.status_code == 200:
124 | print('推送成功!')
125 | else:
126 | print('推送失败!')
127 | except requests.RequestException:
128 | print('推送失败!')
129 |
130 |
131 | # 解析字符串
132 | def parse_string(input_string, error_message):
133 | parts = input_string.split('@')
134 | if len(parts) < 2:
135 | raise ValueError(error_message)
136 | return parts
137 |
138 |
139 | # 解析账号
140 | def parse_account(account):
141 | parts = parse_string(account, f"账号格式错误: {account}")
142 | username, password = parts[0], parts[1]
143 | uids = parts[2] if len(parts) > 2 and parts[2] else None
144 | return username, password, uids
145 |
146 |
147 | # 解析API配置
148 | def parse_api_config(config):
149 | parts = parse_string(config, f"API配置格式错误: {config}")
150 | trade_no, key = parts[0], parts[1]
151 | return trade_no, key
152 |
153 |
154 | # 配置Chrome选项
155 | def configure_chrome_options(use_proxy, proxy_address, proxy_username, proxy_password):
156 | chrome_options = Options()
157 | chrome_options.add_argument('--no-sandbox')
158 | chrome_options.add_argument('--disable-dev-shm-usage')
159 | chrome_options.add_argument('--disable-gpu')
160 | chrome_options.add_argument('--disable-software-rasterizer')
161 | # chrome_options.binary_location = r'D:\Apps\chrome89\Chrome-bin\chrome.exe'
162 | if use_proxy:
163 | proxy_host, proxy_port = proxy_address.split(':')
164 | proxy_plugin = create_proxy_extension(proxy_host, proxy_port, proxy_username, proxy_password)
165 | chrome_options.add_extension(proxy_plugin)
166 | return chrome_options
167 |
168 |
169 | class Juliang_net(Tencent):
170 | """
171 | 基于巨量IP的签到类
172 | """
173 |
174 | def __init__(self, url, username, password, browser, ttshitu_username, ttshitu_password):
175 | super().__init__(ttshitu_username, ttshitu_password, browser)
176 | self.browser = browser # 设置浏览器对象
177 | self.url = url
178 | self.username = username
179 | self.password = password
180 | self.wait = WebDriverWait(self.browser, 30) # 显式等待 30 秒
181 |
182 | def set_info(self):
183 | """
184 | 填写表单信息
185 | """
186 | self.browser.get(url=self.url)
187 | try:
188 | # 输入账号密码
189 | print("正在输入用户名和密码...")
190 | input_username = self.wait.until(EC.presence_of_element_located((By.NAME, 'username')))
191 | input_password = self.wait.until(EC.presence_of_element_located((By.NAME, 'password')))
192 | input_username.send_keys(self.username)
193 | input_password.send_keys(self.password)
194 |
195 | # 点击登录按钮
196 | print("单击登录按钮...")
197 | login_button = self.browser.find_element(By.ID, 'login')
198 | login_button.click()
199 |
200 | # 增加延迟,确保页面成功登录
201 | time.sleep(5)
202 |
203 | print("正在等待可单击的签到按钮...")
204 | sign_button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'free_card_2')))
205 | sign_button.click()
206 | print("已单击“签到”按钮")
207 | time.sleep(10)
208 |
209 | except Exception as e:
210 | print('错误:', e)
211 | print("准备重试...")
212 | self.set_info()
213 |
214 | # 构建URL
215 | @staticmethod
216 | def build_api_url(trade_no, key, num=1, **options):
217 | """
218 | 构建URL
219 | """
220 | params = {
221 | 'trade_no': trade_no,
222 | 'num': num,
223 | **options
224 | }
225 |
226 | sign = Juliang_net.md5_sign(params, key)
227 | query_string = urllib.parse.urlencode(params) + "&sign=" + sign
228 |
229 | url = f'http://v2.api.juliangip.com/dynamic/getips?{query_string}'
230 | return url
231 |
232 | @staticmethod
233 | def md5_sign(params, secret):
234 | sign_content = Juliang_net.get_sign_content(params)
235 | return hashlib.md5((sign_content + '&key=' + secret).encode('utf-8')).hexdigest()
236 |
237 | @staticmethod
238 | def get_sign_content(params):
239 | params.pop('sign', None) # 删除 sign
240 | sorted_params = sorted(params.items())
241 | sign_content = '&'.join(
242 | [f"{k}={str(v)}" for k, v in sorted_params if str(v) is not None and not str(v).startswith('@')])
243 | return sign_content
244 |
245 | # def get_url(self, api_key):
246 | # """
247 | # 获取API URL
248 | # """
249 | # try:
250 | # # 进入 “包时” 页面
251 | # self.browser.get('https://www.juliangip.com/users/product/time')
252 |
253 | # # 增加延迟确保页面完全载入
254 | # time.sleep(5)
255 |
256 | # # 进入提取页
257 | # get_url_page = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div[2]/table/tbody/tr/td[9]/a[1]')))
258 | # get_url_page.click()
259 |
260 | # # 继续延迟3秒
261 | # time.sleep(3)
262 |
263 | # # 控制滚动页面的高度
264 | # target_height = 700 # 目标滚动高度
265 | # scroll_increment = 250 # 每次滚动的高度
266 | # current_position = 0
267 |
268 | # while current_position < target_height:
269 | # self.browser.execute_script(f"window.scrollTo(0, {current_position});")
270 | # current_position += scroll_increment
271 | # time.sleep(1) # 等待滚动加载完成
272 |
273 | # # 点击提取API按钮
274 | # extract_button = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//*[@id="api"]/div[1]/div[1]/div[1]/div[2]/form/div[11]/div/button/span')))
275 | # extract_button.click()
276 |
277 | # time.sleep(3)
278 |
279 | # # 关闭弹窗
280 | # quit_msg = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'el-message-box__close')))
281 | # quit_msg.click()
282 |
283 | # # 清空剪贴板内容
284 | # pyperclip.copy('')
285 |
286 | # time.sleep(2)
287 |
288 | # # 点击复制链接按钮
289 | # copy_button = self.wait.until(EC.element_to_be_clickable((By.CLASS_NAME, 'el-icon-document-copy')))
290 | # copy_button.click()
291 |
292 | # # 增加延迟以确保内容复制完成
293 | # time.sleep(2)
294 |
295 | # # 获取剪贴板内容
296 | # base_url = pyperclip.paste()
297 | # print("url:", base_url)
298 |
299 | # # 定位业务编号元素
300 | # trade_no_element = self.wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="app"]/div[2]/table/tbody/tr/td[2]')))
301 | # # 获取业务编号
302 | # trade_no = trade_no_element.text
303 |
304 | # # 业务编号是否为空
305 | # if not trade_no:
306 | # print("业务编号为空")
307 | # return None
308 |
309 | # #构建URL
310 | # new_url = Juliang_net.build_url(trade_no, 1, api_key, sqlit=2)
311 | # print("api url:", new_url)
312 |
313 | # except Exception as e:
314 | # print(f"操作时出错:{e}")
315 | # return None
316 |
317 |
318 | # 主函数
319 | def main():
320 | # 加载配置文件
321 | config = read_config('config.json') or exit("无法加载配置。")
322 |
323 | # 读取auto-proxy-pool配置
324 | proxy_pool_config = get_config(config, 'auto_proxy_pool_config')
325 | proxy_pool_url = proxy_pool_config['proxy_pool_url']
326 | auth = proxy_pool_config['auth']
327 |
328 | # 读取巨量IP账号和API配置
329 | juliang_account = get_config(config, 'juliang_account')
330 | juliang_api_config = get_config(config, 'juliang_api_config')
331 |
332 | # 读取wxpusher配置
333 | wxpush_config = get_config(config, 'wxpush_config', {})
334 | appToken = wxpush_config.get('appToken', '')
335 | title = wxpush_config.get('title', '巨量IP签到')
336 |
337 | # 读取shitu配置
338 | ttshitu_config = get_config(config, 'ttshitu_config', {})
339 | ttshitu_username = ttshitu_config.get('username')
340 | ttshitu_password = ttshitu_config.get('password')
341 |
342 | ttshitu = is_config_valid(ttshitu_config, ['username', 'password'])
343 | if not ttshitu:
344 | raise ValueError("未配置ttshitu")
345 |
346 | # 读取代理配置
347 | proxy_config = get_config(config, 'proxy_config', {})
348 |
349 | # 检查代理配置是否有效
350 | use_proxy = is_config_valid(proxy_config, ['proxy_address', 'proxy_username', 'proxy_password'])
351 | if use_proxy:
352 | proxy_address = proxy_config['proxy_address']
353 | proxy_username = proxy_config['proxy_username']
354 | proxy_password = proxy_config['proxy_password']
355 | print("使用代理设置:")
356 | print(f"IP: {proxy_address}")
357 | print(f"用户名: {proxy_username}")
358 | print(f"密码: {proxy_password}")
359 | else:
360 | proxy_address = None
361 | proxy_username = None
362 | proxy_password = None
363 | print("代理配置不完整,不使用代理。")
364 |
365 | # 开始执行签到操作
366 | if len(juliang_account) != len(juliang_api_config):
367 | raise ValueError("账号和API KEY数量不匹配")
368 |
369 | url = 'https://www.juliangip.com/user/login'
370 |
371 | # 定义一个列表来存储api_url
372 | api_url_list = []
373 |
374 | for account, api_config in zip(juliang_account, juliang_api_config):
375 | print(f"正在处理帐户: {account}")
376 |
377 | username, password, uids = parse_account(account)
378 | trade_no, api_key = parse_api_config(api_config)
379 |
380 | # 配置Chrome选项
381 | chrome_options = configure_chrome_options(use_proxy, proxy_address, proxy_username, proxy_password)
382 |
383 | # 手动指定chromedriver路径
384 | # D:\Apps\chrome89\Chrome-bin\chromedriver.exe
385 | # C:\Users\windowsuser\AppData\Local\Google\Chrome\Application\chromedriver.exe
386 | chromedriver_path = r'D:\Apps\chrome89\Chrome-bin\chromedriver.exe'
387 | service = Service(chromedriver_path)
388 |
389 | # 创建浏览器实例
390 | try:
391 | browser = webdriver.Chrome(service=service, options=chrome_options)
392 | browser.set_page_load_timeout(30) # 设置页面加载超时时间为30秒
393 | tencent = Juliang_net(url, username, password, browser, ttshitu_username, ttshitu_password)
394 |
395 | # 过滑块验证
396 | tencent.re_start()
397 |
398 | # 登录填表
399 | # tencent.set_info()
400 |
401 | # 提取url操作
402 | api_url = tencent.build_api_url(trade_no, api_key, split=2, area='四川')
403 |
404 | # 将生成的 api_url 添加到列表中
405 | api_url_list.append(api_url)
406 |
407 | # wxpusher消息推送
408 | if uids:
409 | wxpush(title, api_url, [uids], appToken)
410 |
411 | except Exception as e:
412 | raise Exception(f"处理帐户 {username} 时发生错误: {e}")
413 |
414 | finally:
415 | # 确保关闭浏览器
416 | if 'tencent' in locals():
417 | tencent.end()
418 |
419 | # 检查auto-proxy-pool配置是否有效
420 | use_save_pool = is_config_valid(proxy_pool_config, ['proxy_pool_url', 'auth'])
421 | if use_save_pool:
422 | # 保存auto-proxy-pool
423 | set_proxy_pool(proxy_pool_url, api_url_list, auth)
424 |
425 |
426 | if __name__ == '__main__':
427 | main()
428 |
--------------------------------------------------------------------------------