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("未发现登录状态,请手动操作并将状态保存...") # 注意:login 会启动一个新的浏览器窗口 await auth.login(target_url) # 登录完成后,我们需要关闭当前空的 context 并重新加载带有 cookie 的 context 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. 抢购核心逻辑 (H5 流程) # 注意:这里的选择器需要根据微店 H5 实际页面结构进行微调 # 这里演示一般的微店抢购流程:点击购买 -> 选择规格 -> 确定 -> 提交订单 try: # 刷新页面以获取最新状态(可选,视具体页面倒计时逻辑而定) # await page.reload() # 点击“立即购买”按钮 # 微店 H5 常见的购买按钮类名类似于 .buy-btn, .footer-buy # 我们尝试使用 text 匹配 buy_button = page.get_by_text("立即购买") await buy_button.click() print("点击立即购买") # 处理 SKU 选择(如果弹出 SKU 选择框) # 这里简单起见,如果弹出了规格选择,点击第一个选项并确定 # 实际需要根据 config 中的 sku_id 进行精准点击 confirm_btn = page.get_by_text("确定") if await confirm_btn.is_visible(): await confirm_btn.click() print("点击确定(SKU)") # 进入确认订单页面后,点击“提交订单” # 提交订单按钮通常在底部,文字为“提交订单” submit_btn = page.get_by_text("提交订单") await submit_btn.wait_for(state="visible", timeout=5000) await submit_btn.click() print("点击提交订单!抢购请求已发送!") except Exception as e: 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))