import asyncio import yaml import time from playwright.async_api import async_playwright from utils.stealth import stealth_async from utils.auth import Authenticator from utils.timer import PrecisionTimer async def snatch(config): auth = Authenticator(config.get("auth_file", "auth_state.json")) timer = PrecisionTimer() timer.sync_time() async with async_playwright() as p: browser, context = await auth.get_context(p, headless=config.get("headless", False)) page = await context.new_page() await stealth_async(page) target_url = config.get("target_url") if not target_url: print("错误: 未配置 target_url") return # 1. 预热:先打开页面 print(f"正在打开商品页面: {target_url}") await page.goto(target_url) # 如果未登录,处理登录 if not auth.has_auth(): print("未发现登录状态,请手动操作并将状态保存...") await auth.login(target_url) await context.close() await browser.close() browser, context = await auth.get_context(p, headless=config.get("headless", False)) page = await context.new_page() await stealth_async(page) print("已重新加载登录状态,正在打开商品页面...") await page.goto(target_url) # 2. 等待抢购时间 snatch_time = config.get("snatch_time") if snatch_time: await timer.wait_until(snatch_time) # 3. 抢购核心逻辑 max_retries = 3 for attempt in range(max_retries): try: # 刷新页面,让预售按钮变为可点击 if attempt > 0: await asyncio.sleep(0.3) await page.reload(wait_until='domcontentloaded', timeout=10000) await asyncio.sleep(0.5) # 点击购买按钮(兼容多种文案) buy_btn = None for text in ["立即抢购", "立即购买", "马上抢", "立即秒杀"]: loc = page.get_by_text(text, exact=False) if await loc.count() > 0: buy_btn = loc.first break if not buy_btn: if attempt < max_retries - 1: print(f"第{attempt+1}次未找到购买按钮,重试...") continue print("错误: 未找到购买按钮") break await buy_btn.click(timeout=3000) print("点击购买按钮") # 处理 SKU 选择(如果弹出规格选择框) await asyncio.sleep(0.5) try: confirm_btn = page.get_by_text("确定", exact=True) if await confirm_btn.count() > 0 and await confirm_btn.first.is_visible(): # 自动选择第一个可用的 SKU 选项 sku_items = page.locator('.sku-item:not(.disabled), .sku_item:not(.disabled), [class*="sku"] [class*="item"]:not([class*="disabled"])') if await sku_items.count() > 0: await sku_items.first.click() print("自动选择 SKU") await asyncio.sleep(0.3) await confirm_btn.first.click(timeout=3000) print("点击确定(SKU)") except Exception: pass # 提交订单 submit_btn = page.get_by_text("提交订单") await submit_btn.wait_for(state="visible", timeout=8000) await submit_btn.click() print("点击提交订单!抢购请求已发送!") break except Exception as e: if attempt < max_retries - 1: print(f"第{attempt+1}次尝试失败: {e},重试...") else: print(f"抢购失败: {e}") # 保持浏览器打开一段时间查看结果 await asyncio.sleep(10) await browser.close() def load_config(): with open("config.yaml", "r", encoding="utf-8") as f: return yaml.safe_load(f) if __name__ == "__main__": config = load_config() asyncio.run(snatch(config))