|
|
@@ -7,6 +7,8 @@ import socket
|
|
|
import shutil
|
|
|
import random
|
|
|
import requests
|
|
|
+import argparse
|
|
|
+import concurrent.futures
|
|
|
from urllib.parse import urlencode
|
|
|
from datetime import datetime, timedelta
|
|
|
from typing import Optional, Dict
|
|
|
@@ -18,6 +20,7 @@ from vs_types import NotFoundError, PermissionDeniedError, RateLimiteddError, Se
|
|
|
from utils.mouse import HumanMouse
|
|
|
from utils.keyboard import HumanKeyboard
|
|
|
from utils.scroll import HumanScroll
|
|
|
+from utils.fingerprint_utils import FingerprintGenerator
|
|
|
|
|
|
def generate_random_account_detail() -> Dict:
|
|
|
"""基于 randomuser 生成随机账户信息,并保留指定固定字段。"""
|
|
|
@@ -110,6 +113,20 @@ def generate_random_account_detail() -> Dict:
|
|
|
}
|
|
|
except Exception:
|
|
|
return default_payload
|
|
|
+
|
|
|
+def load_proxies(pool_name):
|
|
|
+ """从 config/proxies.json 读取对应的代理池"""
|
|
|
+ config_path = os.path.join(os.path.dirname(__file__), 'config', 'proxies.json')
|
|
|
+ try:
|
|
|
+ with open(config_path, 'r', encoding='utf-8') as f:
|
|
|
+ data = json.load(f)
|
|
|
+ proxies = data.get(pool_name, [])
|
|
|
+ if not proxies:
|
|
|
+ raise ValueError(f"代理池 '{pool_name}' 为空或不存在!")
|
|
|
+ return proxies
|
|
|
+ except Exception as e:
|
|
|
+ print(f"读取代理配置文件失败: {e}")
|
|
|
+ exit(1)
|
|
|
|
|
|
class TlsRegistrator:
|
|
|
def __init__(self, tls_url, proxy_config: Optional[Dict]=None, capsolver_key: Optional[str]=None, account_detail: Optional[Dict]=None):
|
|
|
@@ -117,9 +134,9 @@ class TlsRegistrator:
|
|
|
self.capsolver_key = capsolver_key
|
|
|
self.account_detail = account_detail
|
|
|
# 隔离的用户数据目录
|
|
|
- #self.instance_id = uuid.uuid4().hex[:8]
|
|
|
+ self.instance_id = uuid.uuid4().hex[:8]
|
|
|
self.tls_url = tls_url
|
|
|
- self.instance_id = '18d389e9'
|
|
|
+ # self.instance_id = '18d389e9'
|
|
|
self.workspace = os.path.abspath(os.path.join("data", f"reg_session_{self.instance_id}"))
|
|
|
self.page = None
|
|
|
self.mouse = None
|
|
|
@@ -155,13 +172,19 @@ class TlsRegistrator:
|
|
|
self._log(f"Setting proxy: {p['ip']}:{p['port']}")
|
|
|
co.set_proxy(proxy_str)
|
|
|
|
|
|
+ fingerprint_gen = FingerprintGenerator()
|
|
|
+ specific_fp = fingerprint_gen.generate(self.instance_id)
|
|
|
+ self._log(f'browser fingerprint={specific_fp}')
|
|
|
# 3. 反爬及稳定性配置
|
|
|
co.headless(False)
|
|
|
co.set_argument('--no-sandbox')
|
|
|
- co.set_argument('--disable-gpu')
|
|
|
+ # co.set_argument('--disable-gpu')
|
|
|
co.set_argument('--disable-dev-shm-usage')
|
|
|
co.set_argument('--window-size=1920,1080')
|
|
|
co.set_argument('--disable-blink-features=AutomationControlled')
|
|
|
+ co.set_argument(f"--fingerprint={specific_fp.get('seed')}")
|
|
|
+ co.set_argument(f"--fingerprint-platform={specific_fp.get('platform')}")
|
|
|
+ co.set_argument(f"--fingerprint-brand={specific_fp.get('brand')}")
|
|
|
self.page = ChromiumPage(co)
|
|
|
self.page.get(self.tls_url)
|
|
|
cf_bypasser = CloudflareBypasser(self.page, log=True)
|
|
|
@@ -615,32 +638,104 @@ class TlsRegistrator:
|
|
|
if os.path.exists(self.workspace):
|
|
|
time.sleep(1) # 等待文件锁释放
|
|
|
shutil.rmtree(self.workspace, ignore_errors=True)
|
|
|
-
|
|
|
-
|
|
|
-if __name__ == "__main__":
|
|
|
- # ================= 配置区域 =================
|
|
|
-
|
|
|
- PROXY_CONFIG = {
|
|
|
- "scheme": "http",
|
|
|
- "ip": "127.0.0.1",
|
|
|
- "port": "7890",
|
|
|
- "username": "",
|
|
|
- "password": ""
|
|
|
- }
|
|
|
+
|
|
|
+def register_worker(proxy_config, tls_url, capsolver_key):
|
|
|
+ """单个注册任务的工作线程函数"""
|
|
|
+ # account_detail = generate_random_account_detail() # 替换为你的实际调用
|
|
|
+ account_detail = {"email": f"test_{random.randint(1000,9999)}@test.com"}
|
|
|
|
|
|
- CAPSOLVER_KEY = "CAP-5441DD341DD3CC2FAEF0BE6FE493EE9A"
|
|
|
- TLS_URL = "https://visas-fr.tlscontact.com/en-us/country/gb/vac/gbLON2fr"
|
|
|
- ACCOUNT_DETAIL = generate_random_account_detail()
|
|
|
- # ============================================
|
|
|
+ bot = None
|
|
|
try:
|
|
|
- bot = TlsRegistrator(TLS_URL, proxy_config=PROXY_CONFIG, capsolver_key=CAPSOLVER_KEY, account_detail=ACCOUNT_DETAIL)
|
|
|
- bot.init_browser()
|
|
|
+ bot = TlsRegistrator(
|
|
|
+ tls_url,
|
|
|
+ proxy_config=proxy_config,
|
|
|
+ capsolver_key=capsolver_key,
|
|
|
+ account_detail=account_detail
|
|
|
+ )
|
|
|
+ bot.init_browser() # ⚠️ 记得在 Docker 中必须是 headless 无头模式
|
|
|
+
|
|
|
now_utc = datetime.utcnow()
|
|
|
sent_at = now_utc.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
+
|
|
|
bot.register()
|
|
|
bot.activate(sent_at=sent_at)
|
|
|
bot.make_account_useful()
|
|
|
bot.upload_account_to_server()
|
|
|
+
|
|
|
+ print(f"[SUCCESS] 账号 {account_detail.get('email')} 注册成功! 使用代理: {proxy_config.get('ip')}")
|
|
|
+ return True
|
|
|
+
|
|
|
except Exception as e:
|
|
|
- print(f'Exception Info={e}')
|
|
|
- time.sleep(3600)
|
|
|
+ print(f"[ERROR] 注册失败 | 代理 IP: {proxy_config.get('ip')} | 异常信息: {e}")
|
|
|
+ return False
|
|
|
+
|
|
|
+ finally:
|
|
|
+ # ⚠️ 极度重要:无论成功或失败,必须关闭浏览器释放内存!
|
|
|
+ if bot and hasattr(bot, 'close'):
|
|
|
+ try: bot.close()
|
|
|
+ except: pass
|
|
|
+ elif bot and hasattr(bot, 'quit'):
|
|
|
+ try: bot.quit()
|
|
|
+ except: pass
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ # ================= 命令行参数解析 =================
|
|
|
+ parser = argparse.ArgumentParser(description="TLS 批量注册机")
|
|
|
+ parser.add_argument("-n", "--concurrency", type=int, default=5, help="最大并发数 (N)")
|
|
|
+ parser.add_argument("-m", "--target", type=int, default=10, help="最大成功注册数 (M)")
|
|
|
+ parser.add_argument("-p", "--pool", type=str, default="isp_all", help="代理池名称")
|
|
|
+ parser.add_argument("-u", "--url", type=str, default="https://visas-fr.tlscontact.com/en-us/country/gb/vac/gbLON2fr", help="TLS 目标网址")
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ # ================= 环境变量读取 =================
|
|
|
+ capsolver_key = os.getenv("CAPSOLVER_KEY")
|
|
|
+ if not capsolver_key:
|
|
|
+ print("[FATAL] 未设置环境变量 CAPSOLVER_KEY,程序退出!")
|
|
|
+ exit(1)
|
|
|
+
|
|
|
+ print(f"[*] 启动注册任务 | 目标数: {args.target} | 并发数: {args.concurrency} | 代理池: {args.pool} | URL: {args.url}")
|
|
|
+
|
|
|
+ proxies = load_proxies(args.pool)
|
|
|
+ print(f"[*] 成功加载代理数量: {len(proxies)} 个")
|
|
|
+
|
|
|
+ success_count = 0
|
|
|
+ active_tasks = 0
|
|
|
+
|
|
|
+ # 使用线程池维持并发
|
|
|
+ with concurrent.futures.ThreadPoolExecutor(max_workers=args.concurrency) as executor:
|
|
|
+ futures = {}
|
|
|
+
|
|
|
+ # 1. 初始填充任务队列
|
|
|
+ while active_tasks < args.concurrency and (success_count + active_tasks) < args.target:
|
|
|
+ proxy = random.choice(proxies)
|
|
|
+ fut = executor.submit(register_worker, proxy, args.url, capsolver_key)
|
|
|
+ futures[fut] = proxy
|
|
|
+ active_tasks += 1
|
|
|
+
|
|
|
+ # 2. 调度循环
|
|
|
+ while futures:
|
|
|
+ done, _ = concurrent.futures.wait(futures, return_when=concurrent.futures.FIRST_COMPLETED)
|
|
|
+
|
|
|
+ for fut in done:
|
|
|
+ proxy = futures.pop(fut)
|
|
|
+ active_tasks -= 1
|
|
|
+
|
|
|
+ try:
|
|
|
+ if fut.result():
|
|
|
+ success_count += 1
|
|
|
+ print(f"[*] 进度更新: {success_count} / {args.target}")
|
|
|
+ except Exception as e:
|
|
|
+ print(f"[FATAL] 线程未捕获异常: {e}")
|
|
|
+
|
|
|
+ # 如果还没达到目标,补入新任务
|
|
|
+ if (success_count + active_tasks) < args.target:
|
|
|
+ new_proxy = random.choice(proxies)
|
|
|
+ new_fut = executor.submit(register_worker, new_proxy, args.url, capsolver_key)
|
|
|
+ futures[new_fut] = new_proxy
|
|
|
+ active_tasks += 1
|
|
|
+
|
|
|
+ print(f"[*] 任务结束!共成功注册 {success_count} 个账号。")
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|