├── .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 | 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 | 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 | 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 | --------------------------------------------------------------------------------