StarrySkyWorld commited on
Commit
86d4562
·
1 Parent(s): 350358b

feat(main): 替换SeleniumBase为Playwright并增加超时设置

Browse files

引入Playwright并移除SeleniumBase的依赖。
增加导航和配置等待的超时设置。
修改配置加载逻辑以适应新的超时参数。
更新注册流程以使用Playwright API。
调整验证码获取和姓名输入部分的实现。
优化错误截图保存逻辑以适应Playwright。
改进session管理,确保资源正确关闭。
添加窗口尺寸解析函数。

Files changed (1) hide show
  1. main.py +192 -109
main.py CHANGED
@@ -1,12 +1,12 @@
1
- from seleniumbase import Driver
2
- from selenium.webdriver.common.by import By
3
- from selenium.webdriver.support.ui import WebDriverWait
4
- from selenium.webdriver.support import expected_conditions as EC
5
  from bs4 import BeautifulSoup
6
  from urllib.parse import urlparse, parse_qs
7
  from datetime import datetime
8
  import time, random, json, os, requests
9
 
 
 
10
  # 配置
11
  DEFAULT_CONFIG = {
12
  "total_accounts": 20,
@@ -14,10 +14,12 @@ DEFAULT_CONFIG = {
14
  "mail_key": "gpt-test",
15
  "output_dir": "gemini_accounts",
16
  "login_url": "https://auth.business.gemini.google/login?continueUrl=https:%2F%2Fbusiness.gemini.google%2F&wiffid=CAoSJDIwNTlhYzBjLTVlMmMtNGUxZC1hY2JkLThmOGY2ZDE0ODM1Mg",
17
- "headless": False,
18
  "window_size": "1366,768",
19
  "lang": "zh-CN",
20
- "timezone": "Asia/Shanghai"
 
 
21
  }
22
 
23
  TOTAL_ACCOUNTS = DEFAULT_CONFIG["total_accounts"]
@@ -29,6 +31,8 @@ HEADLESS = DEFAULT_CONFIG["headless"]
29
  WINDOW_SIZE = DEFAULT_CONFIG["window_size"]
30
  BROWSER_LANG = DEFAULT_CONFIG["lang"]
31
  TIMEZONE = DEFAULT_CONFIG["timezone"]
 
 
32
  SCREENSHOT_DIR = "screenshots"
33
 
34
  # XPath
@@ -41,6 +45,38 @@ XPATH = {
41
  NAMES = ["James Smith", "John Johnson", "Robert Williams", "Michael Brown", "William Jones",
42
  "David Garcia", "Mary Miller", "Patricia Davis", "Jennifer Rodriguez", "Linda Martinez"]
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  def log(msg, level="INFO"): print(f"[{level}] {msg}")
45
 
46
  def load_config(path="config.json"):
@@ -63,16 +99,18 @@ def expand_env_vars(value):
63
  return value
64
 
65
  def apply_config(cfg):
66
- global TOTAL_ACCOUNTS, MAIL_API, MAIL_KEY, OUTPUT_DIR, LOGIN_URL, HEADLESS, WINDOW_SIZE, BROWSER_LANG, TIMEZONE
67
  TOTAL_ACCOUNTS = int(cfg.get("total_accounts", TOTAL_ACCOUNTS))
68
  MAIL_API = cfg.get("mail_api", MAIL_API)
69
  MAIL_KEY = cfg.get("mail_key", MAIL_KEY)
70
  OUTPUT_DIR = cfg.get("output_dir", OUTPUT_DIR)
71
  LOGIN_URL = cfg.get("login_url", LOGIN_URL)
72
- HEADLESS = cfg.get("headless", HEADLESS)
73
  WINDOW_SIZE = cfg.get("window_size", WINDOW_SIZE)
74
  BROWSER_LANG = cfg.get("lang", BROWSER_LANG)
75
  TIMEZONE = cfg.get("timezone", TIMEZONE)
 
 
76
 
77
  def create_email():
78
  """创建临时邮箱"""
@@ -87,7 +125,7 @@ def create_email():
87
  log(f"创建邮箱失败: {e}", "ERR")
88
  return None
89
 
90
- def get_code(email, driver, timeout=30):
91
  """获取验证码"""
92
  log(f"等待验证码 (最多{timeout}s)...")
93
  start = time.time()
@@ -112,92 +150,127 @@ def get_code(email, driver, timeout=30):
112
  log("验证码超时", "ERR")
113
  return None
114
 
115
- def save_config(email, cookies, url):
116
- """保存配置"""
117
  os.makedirs(OUTPUT_DIR, exist_ok=True)
118
- parsed = urlparse(url)
119
- path_parts = url.split('/')
120
- config_id = None
121
- for i, p in enumerate(path_parts):
122
- if p == 'cid' and i+1 < len(path_parts):
123
- config_id = path_parts[i+1]
124
- # 清理 config_id 结尾的 ?csesidx=xxx
125
- if config_id and '?' in config_id:
126
- config_id = config_id.split('?')[0]
127
- break
128
-
129
- cookie_dict = {c['name']: c for c in cookies}
130
- ses_cookie = cookie_dict.get('__Secure-C_SES', {})
131
-
132
- data = {
133
- "id": email,
134
- "csesidx": parse_qs(parsed.query).get('csesidx', [None])[0],
135
- "config_id": config_id,
136
- "secure_c_ses": ses_cookie.get('value'),
137
- "host_c_oses": cookie_dict.get('__Host-C_OSES', {}).get('value'),
138
- "expires_at": datetime.fromtimestamp(ses_cookie.get('expiry', 0) - 43200).strftime('%Y-%m-%d %H:%M:%S') if ses_cookie.get('expiry') else None
139
- }
140
 
141
- with open(f"{OUTPUT_DIR}/{email}.json", 'w') as f:
142
- json.dump(data, f, indent=2, ensure_ascii=False)
143
- log(f"配置已保存: {email}.json")
144
- return data
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
- def save_error_screenshot(driver, email):
147
  """失败时保存截图"""
148
  safe_email = email or "unknown"
149
  os.makedirs(SCREENSHOT_DIR, exist_ok=True)
150
  filename = f"{safe_email}_err.png"
151
  path = os.path.join(SCREENSHOT_DIR, filename)
152
  try:
153
- driver.save_screenshot(path)
154
  log(f"失败截图已保存: {path}", "WARN")
155
  return filename
156
  except Exception as e:
157
  log(f"保存失败截图出错: {e}", "WARN")
158
  return None
159
 
160
- def register(driver):
161
  """注册单个账号"""
162
  email = create_email()
163
  if not email: return None, False, None
164
 
165
- wait = WebDriverWait(driver, 60)
166
-
167
  # 1. 访问登录页
168
- driver.get(LOGIN_URL)
 
 
169
  time.sleep(5)
170
 
171
  # 2. 输入邮箱
172
  log("输入邮箱...")
173
- inp = wait.until(EC.visibility_of_element_located((By.XPATH, XPATH["email_input"])))
174
- inp.click(); time.sleep(0.3); inp.clear(); time.sleep(0.3)
175
- for c in email: inp.send_keys(c); time.sleep(0.05)
176
- log(f"邮箱: {email}, 实际值: {inp.get_attribute('value')}")
 
 
 
 
177
  time.sleep(1)
178
 
179
  # 3. 点击继续
180
- btn = wait.until(EC.element_to_be_clickable((By.XPATH, XPATH["continue_btn"])))
181
- driver.execute_script("arguments[0].click();", btn)
182
  log("点击继续")
183
  time.sleep(3)
 
184
 
185
  # 4. 获取验证码
186
- code = get_code(email, driver)
187
  if not code: return email, False, None
188
 
189
  # 5. 输入验证码
190
  time.sleep(2)
191
  log(f"输入验证码: {code}")
192
  try:
193
- pin = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='pinInput']")))
194
- pin.click(); time.sleep(0.2)
195
- for c in code: pin.send_keys(c); time.sleep(0.1)
196
- except:
 
197
  try:
198
- span = driver.find_element(By.CSS_SELECTOR, "span[data-index='0']")
199
- span.click(); time.sleep(0.3)
200
- driver.switch_to.active_element.send_keys(code)
201
  except Exception as e:
202
  log(f"验证码输入失败: {e}", "ERR")
203
  return email, False, None
@@ -205,24 +278,28 @@ def register(driver):
205
  # 6. 点击验证
206
  time.sleep(1)
207
  try:
208
- vbtn = driver.find_element(By.XPATH, XPATH["verify_btn"])
209
- driver.execute_script("arguments[0].click();", vbtn)
210
- except:
211
- for btn in driver.find_elements(By.TAG_NAME, "button"):
212
- if '验证' in btn.text: driver.execute_script("arguments[0].click();", btn); break
 
213
  log("点击验证")
214
  time.sleep(5)
 
215
 
216
  # 7. 输入姓名
217
  try:
218
- name_inp = WebDriverWait(driver, 30).until(EC.visibility_of_element_located(
219
- (By.CSS_SELECTOR, "input[formcontrolname='fullName'], input[placeholder='全名'], input#mat-input-0")))
 
 
220
  name = random.choice(NAMES)
221
- name_inp.clear(); time.sleep(0.3)
222
- for c in name: name_inp.send_keys(c); time.sleep(0.03)
 
223
  log(f"姓名: {name}")
224
- from selenium.webdriver.common.keys import Keys
225
- name_inp.send_keys(Keys.ENTER)
226
  except Exception as e:
227
  log(f"姓名输入异常: {e}", "WARN")
228
 
@@ -230,95 +307,101 @@ def register(driver):
230
  log("等待工作台...")
231
  time.sleep(6)
232
  for _ in range(30):
233
- if 'business.gemini.google' in driver.current_url and 'auth' not in driver.current_url:
234
  break
235
  time.sleep(2)
236
  time.sleep(3)
 
237
 
238
  # 9. 保存配置
239
- config = save_config(email, driver.get_cookies(), driver.current_url)
240
- log(f"注册成功: {email}")
241
- return email, True, config
 
 
242
 
243
- def create_driver():
244
  if TIMEZONE:
245
  os.environ["TZ"] = TIMEZONE
246
  if hasattr(time, "tzset"):
247
  time.tzset()
248
- chrome_bin = os.getenv("CHROME_BIN")
249
- window_size = str(WINDOW_SIZE).strip().lower().replace("x", ",")
250
- chromium_args = [
251
- "--no-sandbox",
252
- "--disable-dev-shm-usage",
253
- "--disable-gpu",
254
- "--disable-software-rasterizer",
255
- f"--window-size={window_size}",
256
- ]
257
  if BROWSER_LANG:
258
- chromium_args.append(f"--lang={BROWSER_LANG}")
259
- return Driver(
260
- uc=True,
261
- headless=HEADLESS,
262
- browser="chrome",
263
- binary_location=chrome_bin,
264
- chromium_arg=chromium_args,
265
- )
 
 
 
 
 
 
 
 
266
 
267
  def register_one_account():
268
- driver = create_driver()
269
  email = None
270
  screenshot = None
271
  try:
272
- email, ok, cfg = register(driver)
273
  if not ok:
274
- screenshot = save_error_screenshot(driver, email)
275
  return email, ok, cfg, screenshot
276
  except Exception:
277
- screenshot = save_error_screenshot(driver, email)
278
  raise
279
  finally:
280
- try: driver.quit()
281
- except: pass
282
 
283
  def main():
284
  cfg = load_config()
285
  apply_config(cfg)
286
  print(f"\n{'='*50}\nGemini Business 批量注册 - 共 {TOTAL_ACCOUNTS} 个\n{'='*50}\n")
287
 
288
- driver = create_driver()
289
  success, fail, accounts = 0, 0, []
290
 
291
  for i in range(TOTAL_ACCOUNTS):
292
  print(f"\n{'#'*40}\n注册 {i+1}/{TOTAL_ACCOUNTS}\n{'#'*40}\n")
293
 
294
  try:
295
- driver.current_url # 检查driver是否有效
 
296
  except:
297
- driver = create_driver()
 
298
 
299
  email = None
300
  try:
301
- email, ok, cfg = register(driver)
302
  if ok: success += 1; accounts.append((email, cfg))
303
  else:
304
- save_error_screenshot(driver, email)
305
  fail += 1
306
  except Exception as e:
307
  log(f"异常: {e}", "ERR"); fail += 1
308
- save_error_screenshot(driver, email)
309
- try: driver.quit()
310
- except: pass
311
- driver = create_driver()
312
 
313
  print(f"\n进度: {i+1}/{TOTAL_ACCOUNTS} | 成功: {success} | 失败: {fail}")
314
 
315
  if i < TOTAL_ACCOUNTS - 1:
316
- try: driver.delete_all_cookies()
317
- except: pass
318
  time.sleep(random.randint(3, 5))
319
 
320
- try: driver.quit()
321
- except: pass
322
 
323
  print(f"\n{'='*50}\n完成! 成功: {success}, 失败: {fail}\n配置保存在: {OUTPUT_DIR}/\n{'='*50}")
324
 
 
1
+ from camoufox.sync_api import Camoufox
2
+ from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
 
 
3
  from bs4 import BeautifulSoup
4
  from urllib.parse import urlparse, parse_qs
5
  from datetime import datetime
6
  import time, random, json, os, requests
7
 
8
+ print("Main module loaded")
9
+
10
  # 配置
11
  DEFAULT_CONFIG = {
12
  "total_accounts": 20,
 
14
  "mail_key": "gpt-test",
15
  "output_dir": "gemini_accounts",
16
  "login_url": "https://auth.business.gemini.google/login?continueUrl=https:%2F%2Fbusiness.gemini.google%2F&wiffid=CAoSJDIwNTlhYzBjLTVlMmMtNGUxZC1hY2JkLThmOGY2ZDE0ODM1Mg",
17
+ "headless": True,
18
  "window_size": "1366,768",
19
  "lang": "zh-CN",
20
+ "timezone": "Asia/Shanghai",
21
+ "nav_timeout_ms": 120000,
22
+ "config_wait_ms": 30000
23
  }
24
 
25
  TOTAL_ACCOUNTS = DEFAULT_CONFIG["total_accounts"]
 
31
  WINDOW_SIZE = DEFAULT_CONFIG["window_size"]
32
  BROWSER_LANG = DEFAULT_CONFIG["lang"]
33
  TIMEZONE = DEFAULT_CONFIG["timezone"]
34
+ NAV_TIMEOUT_MS = DEFAULT_CONFIG["nav_timeout_ms"]
35
+ CONFIG_WAIT_MS = DEFAULT_CONFIG["config_wait_ms"]
36
  SCREENSHOT_DIR = "screenshots"
37
 
38
  # XPath
 
45
  NAMES = ["James Smith", "John Johnson", "Robert Williams", "Michael Brown", "William Jones",
46
  "David Garcia", "Mary Miller", "Patricia Davis", "Jennifer Rodriguez", "Linda Martinez"]
47
 
48
+ def parse_window_size(value, fallback=(1366, 768)):
49
+ try:
50
+ parts = str(value).lower().replace("x", ",").split(",")
51
+ width = int(parts[0].strip())
52
+ height = int(parts[1].strip())
53
+ return width, height
54
+ except Exception:
55
+ return fallback
56
+
57
+ class CamoufoxSession:
58
+ def __init__(self, launch_options, context_options):
59
+ self.camoufox = Camoufox(**launch_options)
60
+ self.browser = self.camoufox.__enter__()
61
+ self.context = self.browser.new_context(**context_options)
62
+ self.page = self.context.new_page()
63
+
64
+ def clear_cookies(self):
65
+ try:
66
+ self.context.clear_cookies()
67
+ except Exception:
68
+ pass
69
+
70
+ def close(self):
71
+ try:
72
+ self.context.close()
73
+ except Exception:
74
+ pass
75
+ try:
76
+ self.camoufox.__exit__(None, None, None)
77
+ except Exception:
78
+ pass
79
+
80
  def log(msg, level="INFO"): print(f"[{level}] {msg}")
81
 
82
  def load_config(path="config.json"):
 
99
  return value
100
 
101
  def apply_config(cfg):
102
+ global TOTAL_ACCOUNTS, MAIL_API, MAIL_KEY, OUTPUT_DIR, LOGIN_URL, HEADLESS, WINDOW_SIZE, BROWSER_LANG, TIMEZONE, NAV_TIMEOUT_MS, CONFIG_WAIT_MS
103
  TOTAL_ACCOUNTS = int(cfg.get("total_accounts", TOTAL_ACCOUNTS))
104
  MAIL_API = cfg.get("mail_api", MAIL_API)
105
  MAIL_KEY = cfg.get("mail_key", MAIL_KEY)
106
  OUTPUT_DIR = cfg.get("output_dir", OUTPUT_DIR)
107
  LOGIN_URL = cfg.get("login_url", LOGIN_URL)
108
+ HEADLESS = True
109
  WINDOW_SIZE = cfg.get("window_size", WINDOW_SIZE)
110
  BROWSER_LANG = cfg.get("lang", BROWSER_LANG)
111
  TIMEZONE = cfg.get("timezone", TIMEZONE)
112
+ NAV_TIMEOUT_MS = int(cfg.get("nav_timeout_ms", NAV_TIMEOUT_MS))
113
+ CONFIG_WAIT_MS = int(cfg.get("config_wait_ms", CONFIG_WAIT_MS))
114
 
115
  def create_email():
116
  """创建临时邮箱"""
 
125
  log(f"创建邮箱失败: {e}", "ERR")
126
  return None
127
 
128
+ def get_code(email, timeout=30):
129
  """获取验证码"""
130
  log(f"等待验证码 (最多{timeout}s)...")
131
  start = time.time()
 
150
  log("验证码超时", "ERR")
151
  return None
152
 
153
+ def save_config(email, page, context, timeout_ms=None):
154
+ """保存配置,轮询等待关键字段"""
155
  os.makedirs(OUTPUT_DIR, exist_ok=True)
156
+ wait_ms = CONFIG_WAIT_MS if timeout_ms is None else timeout_ms
157
+ log(f"等待配置数据 (最多{wait_ms}ms)...")
158
+ start = time.time()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ while (time.time() - start) * 1000 < wait_ms:
161
+ url = page.url
162
+ parsed = urlparse(url)
163
+ path_parts = url.split('/')
164
+ config_id = None
165
+ for i, p in enumerate(path_parts):
166
+ if p == 'cid' and i+1 < len(path_parts):
167
+ config_id = path_parts[i+1]
168
+ if config_id and '?' in config_id:
169
+ config_id = config_id.split('?')[0]
170
+ break
171
+
172
+ cookies = context.cookies()
173
+ cookie_dict = {c['name']: c for c in cookies}
174
+ ses_cookie = cookie_dict.get('__Secure-C_SES', {})
175
+ host_cookie = cookie_dict.get('__Host-C_OSES', {})
176
+ csesidx = parse_qs(parsed.query).get('csesidx', [None])[0]
177
+
178
+ if ses_cookie.get('value') and host_cookie.get('value') and csesidx and config_id:
179
+ expiry = ses_cookie.get('expiry', None)
180
+ if expiry is None:
181
+ expiry = ses_cookie.get('expires', None)
182
+ expires_at = None
183
+ if expiry and expiry > 0:
184
+ expires_at = datetime.fromtimestamp(expiry - 43200).strftime('%Y-%m-%d %H:%M:%S')
185
+
186
+ data = {
187
+ "id": email,
188
+ "csesidx": csesidx,
189
+ "config_id": config_id,
190
+ "secure_c_ses": ses_cookie.get('value'),
191
+ "host_c_oses": host_cookie.get('value'),
192
+ "expires_at": expires_at
193
+ }
194
+ log(f"配置数据已就绪 ({time.time() - start:.1f}s)")
195
+ with open(f"{OUTPUT_DIR}/{email}.json", 'w') as f:
196
+ json.dump(data, f, indent=2, ensure_ascii=False)
197
+ log(f"配置已保存: {email}.json")
198
+ return data
199
+
200
+ time.sleep(1)
201
+
202
+ log(f"保存配置: url={page.url}")
203
+ log(f"Cookie键: {list({c['name'] for c in context.cookies()})}")
204
+ parsed = urlparse(page.url)
205
+ missing = []
206
+ cookie_dict = {c['name']: c for c in context.cookies()}
207
+ if not cookie_dict.get('__Secure-C_SES', {}).get('value'): missing.append('secure_c_ses')
208
+ if not cookie_dict.get('__Host-C_OSES', {}).get('value'): missing.append('host_c_oses')
209
+ if not parse_qs(parsed.query).get('csesidx', [None])[0]: missing.append('csesidx')
210
+ if '/cid/' not in page.url: missing.append('config_id')
211
+ log(f"配置不完整,缺失字段: {', '.join(missing)},跳过: {email}", "WARN")
212
+ return None
213
 
214
+ def save_error_screenshot(page, email):
215
  """失败时保存截图"""
216
  safe_email = email or "unknown"
217
  os.makedirs(SCREENSHOT_DIR, exist_ok=True)
218
  filename = f"{safe_email}_err.png"
219
  path = os.path.join(SCREENSHOT_DIR, filename)
220
  try:
221
+ page.screenshot(path=path, full_page=True)
222
  log(f"失败截图已保存: {path}", "WARN")
223
  return filename
224
  except Exception as e:
225
  log(f"保存失败截图出错: {e}", "WARN")
226
  return None
227
 
228
+ def register(page, context):
229
  """注册单个账号"""
230
  email = create_email()
231
  if not email: return None, False, None
232
 
 
 
233
  # 1. 访问登录页
234
+ log(f"访问登录页: {LOGIN_URL}")
235
+ page.goto(LOGIN_URL, wait_until="domcontentloaded", timeout=NAV_TIMEOUT_MS)
236
+ log(f"页面已加载: {page.url}")
237
  time.sleep(5)
238
 
239
  # 2. 输入邮箱
240
  log("输入邮箱...")
241
+ inp = page.locator(f"xpath={XPATH['email_input']}")
242
+ inp.wait_for(state="visible", timeout=60000)
243
+ inp.click()
244
+ time.sleep(0.3)
245
+ inp.fill("")
246
+ page.keyboard.type(email, delay=50)
247
+ actual_value = inp.input_value()
248
+ log(f"邮箱: {email}, 实际值: {actual_value}")
249
  time.sleep(1)
250
 
251
  # 3. 点击继续
252
+ page.locator(f"xpath={XPATH['continue_btn']}").click()
 
253
  log("点击继续")
254
  time.sleep(3)
255
+ log(f"继续后URL: {page.url}")
256
 
257
  # 4. 获取验证码
258
+ code = get_code(email)
259
  if not code: return email, False, None
260
 
261
  # 5. 输入验证码
262
  time.sleep(2)
263
  log(f"输入验证码: {code}")
264
  try:
265
+ pin = page.wait_for_selector("input[name='pinInput']", timeout=60000)
266
+ pin.click()
267
+ time.sleep(0.2)
268
+ page.keyboard.type(code, delay=100)
269
+ except PlaywrightTimeoutError:
270
  try:
271
+ page.locator("span[data-index='0']").click()
272
+ time.sleep(0.3)
273
+ page.keyboard.type(code, delay=100)
274
  except Exception as e:
275
  log(f"验证码输入失败: {e}", "ERR")
276
  return email, False, None
 
278
  # 6. 点击验证
279
  time.sleep(1)
280
  try:
281
+ page.locator(f"xpath={XPATH['verify_btn']}").click()
282
+ except Exception:
283
+ try:
284
+ page.locator("button", has_text="验证").first.click()
285
+ except Exception:
286
+ pass
287
  log("点击验证")
288
  time.sleep(5)
289
+ log(f"验证后URL: {page.url}")
290
 
291
  # 7. 输入姓名
292
  try:
293
+ name_inp = page.wait_for_selector(
294
+ "input[formcontrolname='fullName'], input[placeholder='全名'], input#mat-input-0",
295
+ timeout=30000,
296
+ )
297
  name = random.choice(NAMES)
298
+ name_inp.fill("")
299
+ time.sleep(0.3)
300
+ page.keyboard.type(name, delay=30)
301
  log(f"姓名: {name}")
302
+ name_inp.press("Enter")
 
303
  except Exception as e:
304
  log(f"姓名输入异常: {e}", "WARN")
305
 
 
307
  log("等待工作台...")
308
  time.sleep(6)
309
  for _ in range(30):
310
+ if 'business.gemini.google' in page.url and 'auth' not in page.url:
311
  break
312
  time.sleep(2)
313
  time.sleep(3)
314
+ log(f"最终URL: {page.url}")
315
 
316
  # 9. 保存配置
317
+ config = save_config(email, page, context)
318
+ if config:
319
+ log(f"注册成功: {email}")
320
+ return email, True, config
321
+ return email, False, None
322
 
323
+ def create_session():
324
  if TIMEZONE:
325
  os.environ["TZ"] = TIMEZONE
326
  if hasattr(time, "tzset"):
327
  time.tzset()
328
+ window_width, window_height = parse_window_size(WINDOW_SIZE)
329
+ camoufox_exec = os.getenv("CAMOUFOX_EXECUTABLE")
330
+ launch_options = {
331
+ "headless": HEADLESS,
332
+ "window": (window_width, window_height),
333
+ }
 
 
 
334
  if BROWSER_LANG:
335
+ launch_options["locale"] = BROWSER_LANG
336
+ if camoufox_exec:
337
+ launch_options["executable_path"] = camoufox_exec
338
+
339
+ context_options = {
340
+ "viewport": {"width": window_width, "height": window_height},
341
+ }
342
+ if BROWSER_LANG:
343
+ context_options["locale"] = BROWSER_LANG
344
+ if TIMEZONE:
345
+ context_options["timezone_id"] = TIMEZONE
346
+
347
+ session = CamoufoxSession(launch_options, context_options)
348
+ session.page.set_default_timeout(NAV_TIMEOUT_MS)
349
+ session.page.set_default_navigation_timeout(NAV_TIMEOUT_MS)
350
+ return session
351
 
352
  def register_one_account():
353
+ session = create_session()
354
  email = None
355
  screenshot = None
356
  try:
357
+ email, ok, cfg = register(session.page, session.context)
358
  if not ok:
359
+ screenshot = save_error_screenshot(session.page, email)
360
  return email, ok, cfg, screenshot
361
  except Exception:
362
+ screenshot = save_error_screenshot(session.page, email)
363
  raise
364
  finally:
365
+ session.close()
 
366
 
367
  def main():
368
  cfg = load_config()
369
  apply_config(cfg)
370
  print(f"\n{'='*50}\nGemini Business 批量注册 - 共 {TOTAL_ACCOUNTS} 个\n{'='*50}\n")
371
 
372
+ session = create_session()
373
  success, fail, accounts = 0, 0, []
374
 
375
  for i in range(TOTAL_ACCOUNTS):
376
  print(f"\n{'#'*40}\n注册 {i+1}/{TOTAL_ACCOUNTS}\n{'#'*40}\n")
377
 
378
  try:
379
+ if session.page.is_closed():
380
+ raise RuntimeError("page已关闭")
381
  except:
382
+ session.close()
383
+ session = create_session()
384
 
385
  email = None
386
  try:
387
+ email, ok, cfg = register(session.page, session.context)
388
  if ok: success += 1; accounts.append((email, cfg))
389
  else:
390
+ save_error_screenshot(session.page, email)
391
  fail += 1
392
  except Exception as e:
393
  log(f"异常: {e}", "ERR"); fail += 1
394
+ save_error_screenshot(session.page, email)
395
+ session.close()
396
+ session = create_session()
 
397
 
398
  print(f"\n进度: {i+1}/{TOTAL_ACCOUNTS} | 成功: {success} | 失败: {fail}")
399
 
400
  if i < TOTAL_ACCOUNTS - 1:
401
+ session.clear_cookies()
 
402
  time.sleep(random.randint(3, 5))
403
 
404
+ session.close()
 
405
 
406
  print(f"\n{'='*50}\n完成! 成功: {success}, 失败: {fail}\n配置保存在: {OUTPUT_DIR}/\n{'='*50}")
407