|
|
@@ -16,12 +16,14 @@ from DrissionPage.common import Keys
|
|
|
from DrissionPage import ChromiumPage, ChromiumOptions
|
|
|
from utils.cloudflare_bypass_for_scraping import CloudflareBypasser
|
|
|
from toolkit.vs_cloud_api import VSCloudApi
|
|
|
+from toolkit.proxy_tunnel import ProxyTunnel
|
|
|
from vs_types import NotFoundError, PermissionDeniedError, RateLimiteddError, SessionExpiredOrInvalidError, BizLogicError
|
|
|
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 生成随机账户信息,并保留指定固定字段。"""
|
|
|
today = datetime.today()
|
|
|
@@ -137,19 +139,34 @@ class TlsRegistrator:
|
|
|
self.instance_id = uuid.uuid4().hex[:8]
|
|
|
self.tls_url = tls_url
|
|
|
# self.instance_id = '18d389e9'
|
|
|
- self.workspace = os.path.abspath(os.path.join("data", f"reg_session_{self.instance_id}"))
|
|
|
+ self.workspace = os.path.abspath(os.path.join("data/temp_browser_data", f"reg_session_{self.instance_id}"))
|
|
|
self.page = None
|
|
|
self.mouse = None
|
|
|
self.keyboard = None
|
|
|
+
|
|
|
+ # 持有隧道实例
|
|
|
+ self.tunnel = None
|
|
|
|
|
|
def _log(self, msg):
|
|
|
- print(f"[TLS-Reg-{self.instance_id}] {msg}")
|
|
|
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
+ print(f"[{now}][TLS-Reg-{self.instance_id}] {msg}")
|
|
|
|
|
|
def _get_free_port(self):
|
|
|
"""获取可用端口,防止 DrissionPage 解析日志报错"""
|
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
|
s.bind(('', 0))
|
|
|
return s.getsockname()[1]
|
|
|
+
|
|
|
+ def save_screenshot(self, name_prefix):
|
|
|
+ try:
|
|
|
+ timestamp = int(time.time())
|
|
|
+ filename = f"{self.instance_id}_{name_prefix}_{timestamp}.jpg"
|
|
|
+ save_path = os.path.join("data", filename)
|
|
|
+ os.makedirs("data", exist_ok=True)
|
|
|
+ self.page.get_screenshot(path=save_path, full_page=False)
|
|
|
+ self._log(f"Screenshot saved to {save_path}")
|
|
|
+ except Exception as e:
|
|
|
+ self._log(f"Failed to save screenshot: {e}")
|
|
|
|
|
|
def init_browser(self):
|
|
|
"""初始化独立、配置好代理的浏览器环境"""
|
|
|
@@ -161,16 +178,23 @@ class TlsRegistrator:
|
|
|
co.set_local_port(port)
|
|
|
co.set_user_data_path(self.workspace)
|
|
|
|
|
|
+ chrome_path = os.getenv("CHROME_BIN")
|
|
|
+ if chrome_path and os.path.exists(chrome_path):
|
|
|
+ co.set_paths(browser_path=chrome_path)
|
|
|
+
|
|
|
# 2. 代理配置 (支持账号密码)
|
|
|
if self.proxy_config and self.proxy_config.get("ip"):
|
|
|
p = self.proxy_config
|
|
|
if p.get("username") and p.get("password"):
|
|
|
- proxy_str = f"{p['username']}:{p['password']}@{p['ip']}:{p['port']}"
|
|
|
+ self.tunnel = ProxyTunnel(p['ip'], p['port'], p['username'], p['password'])
|
|
|
+ local_proxy = self.tunnel.start()
|
|
|
+ self._log(f"Tunnel started at {local_proxy}")
|
|
|
+ co.set_argument(f'--proxy-server={local_proxy}')
|
|
|
else:
|
|
|
proxy_str = f"{p.get('scheme', 'http')}://{p['ip']}:{p['port']}"
|
|
|
-
|
|
|
- self._log(f"Setting proxy: {p['ip']}:{p['port']}")
|
|
|
- co.set_proxy(proxy_str)
|
|
|
+ co.set_argument(f'--proxy-server={proxy_str}')
|
|
|
+ else:
|
|
|
+ self._log("[WARN] No proxy configured!")
|
|
|
|
|
|
fingerprint_gen = FingerprintGenerator()
|
|
|
specific_fp = fingerprint_gen.generate(self.instance_id)
|
|
|
@@ -187,9 +211,10 @@ class TlsRegistrator:
|
|
|
co.set_argument(f"--fingerprint-brand={specific_fp.get('brand')}")
|
|
|
self.page = ChromiumPage(co)
|
|
|
self.page.get(self.tls_url)
|
|
|
+ time.sleep(5)
|
|
|
cf_bypasser = CloudflareBypasser(self.page, log=True)
|
|
|
- cf_bypasser.bypass()
|
|
|
- time.sleep(3)
|
|
|
+ cf_bypasser.bypass(max_retry=8)
|
|
|
+ time.sleep(3)
|
|
|
cf_bypasser.handle_waiting_room()
|
|
|
|
|
|
self._log("正在初始化拟人化工具...")
|
|
|
@@ -340,17 +365,42 @@ class TlsRegistrator:
|
|
|
def fill_date_field(page, selector, date_str):
|
|
|
if not date_str:
|
|
|
return
|
|
|
+
|
|
|
ele = page.ele(selector)
|
|
|
ele.scroll.to_see(center=True)
|
|
|
- ele.click()
|
|
|
- time.sleep(0.2)
|
|
|
+
|
|
|
+ js_detect_format = """
|
|
|
+ const parts = new Intl.DateTimeFormat().formatToParts(new Date(2023, 11, 31));
|
|
|
+ let format = [];
|
|
|
+ for (let part of parts) {
|
|
|
+ if (part.type === 'year') format.push('Y');
|
|
|
+ if (part.type === 'month') format.push('M');
|
|
|
+ if (part.type === 'day') format.push('D');
|
|
|
+ }
|
|
|
+ return format;
|
|
|
+ """
|
|
|
+ date_format = page.run_js(js_detect_format)
|
|
|
+
|
|
|
year, month, day = date_str.split('-')
|
|
|
- page.actions.type(year)
|
|
|
- page.actions.type(Keys.TAB)
|
|
|
+ date_dict = {
|
|
|
+ 'Y': year,
|
|
|
+ 'M': month.zfill(2),
|
|
|
+ 'D': day.zfill(2)
|
|
|
+ }
|
|
|
+ ele.click()
|
|
|
time.sleep(0.1)
|
|
|
- page.actions.type(month)
|
|
|
+ page.actions.type(Keys.LEFT * 3)
|
|
|
time.sleep(0.1)
|
|
|
- page.actions.type(day)
|
|
|
+ for i, char in enumerate(date_format):
|
|
|
+ val = date_dict[char]
|
|
|
+ page.actions.type(val)
|
|
|
+ time.sleep(0.1)
|
|
|
+ if char == 'Y':
|
|
|
+ if i < 2:
|
|
|
+ page.actions.type(Keys.RIGHT)
|
|
|
+ time.sleep(0.1)
|
|
|
+ else:
|
|
|
+ pass
|
|
|
|
|
|
email = self.account_detail.get('email')
|
|
|
password = self.account_detail.get('pwd')
|
|
|
@@ -592,6 +642,7 @@ class TlsRegistrator:
|
|
|
submit_btn = self.page.ele('tag:button@@text():Confirm')
|
|
|
submit_btn.scroll.to_see(center=True)
|
|
|
submit_btn.click()
|
|
|
+ time.sleep(6)
|
|
|
|
|
|
def upload_account_to_server(self):
|
|
|
"""
|
|
|
@@ -641,9 +692,7 @@ class TlsRegistrator:
|
|
|
|
|
|
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"}
|
|
|
-
|
|
|
+ account_detail = generate_random_account_detail()
|
|
|
bot = None
|
|
|
try:
|
|
|
bot = TlsRegistrator(
|
|
|
@@ -661,22 +710,21 @@ def register_worker(proxy_config, tls_url, capsolver_key):
|
|
|
bot.activate(sent_at=sent_at)
|
|
|
bot.make_account_useful()
|
|
|
bot.upload_account_to_server()
|
|
|
-
|
|
|
+ bot.save_screenshot(f'success_{account_detail.get("email")}')
|
|
|
print(f"[SUCCESS] 账号 {account_detail.get('email')} 注册成功! 使用代理: {proxy_config.get('ip')}")
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"[ERROR] 注册失败 | 代理 IP: {proxy_config.get('ip')} | 异常信息: {e}")
|
|
|
+ bot.save_screenshot('tls_registration_failed')
|
|
|
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
|
|
|
+ if bot:
|
|
|
+ try:
|
|
|
+ bot.cleanup()
|
|
|
+ except:
|
|
|
+ pass
|
|
|
|
|
|
|
|
|
def main():
|