|
|
@@ -0,0 +1,103 @@
|
|
|
+from camoufox import Camoufox
|
|
|
+from camoufox.server import launch_server
|
|
|
+from camoufox.async_api import AsyncCamoufox
|
|
|
+from playwright.async_api import Browser,Page
|
|
|
+import asyncio
|
|
|
+import signal
|
|
|
+import os
|
|
|
+import datetime
|
|
|
+from typing import Optional, Dict
|
|
|
+import logging
|
|
|
+from pydantic import BaseModel
|
|
|
+from typing import Optional, Dict
|
|
|
+from config.settings import OUTPUT_DIR, WORK_DIR
|
|
|
+
|
|
|
+class BrowserConfig(BaseModel):
|
|
|
+ """浏览器配置参数模型"""
|
|
|
+ headless: bool = False
|
|
|
+ geoip: bool = True
|
|
|
+ proxy: Optional[Dict] = {'server': 'http://localhost:1881'}
|
|
|
+ init_url: str = "https://www.browserscan.net"
|
|
|
+ screenshot_dir: str = OUTPUT_DIR / "screenshots"
|
|
|
+
|
|
|
+class PageOperations:
|
|
|
+ """封装页面交互操作"""
|
|
|
+ def __init__(self, page: Page, config: BrowserConfig):
|
|
|
+ self.page = page
|
|
|
+ self.config = config
|
|
|
+
|
|
|
+ async def search_element(self, selector: str, timeout: float = 30.0):
|
|
|
+ """等待并返回指定元素"""
|
|
|
+ return await self.page.wait_for_selector(selector, timeout=timeout)
|
|
|
+
|
|
|
+ async def click_element(self, selector: str):
|
|
|
+ """点击指定选择器的元素"""
|
|
|
+ element = await self.search_element(selector)
|
|
|
+ await element.click()
|
|
|
+
|
|
|
+ async def take_screenshot(self, filename: str):
|
|
|
+ """带时间戳的截图保存"""
|
|
|
+ os.makedirs(self.config.screenshot_dir, exist_ok=True)
|
|
|
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
+ path = os.path.join(self.config.screenshot_dir, f"{timestamp}_{filename}")
|
|
|
+ await self.page.screenshot(path=path, full_page=True)
|
|
|
+ return path
|
|
|
+
|
|
|
+ async def fill_input(self, selector: str, text: str):
|
|
|
+ """在指定输入框填充文本"""
|
|
|
+ element = await self.search_element(selector)
|
|
|
+ await element.fill(text)
|
|
|
+
|
|
|
+ async def press_enter(self):
|
|
|
+ """执行键盘回车操作"""
|
|
|
+ await self.page.keyboard.press("Enter")
|
|
|
+
|
|
|
+class CamoufoxManager:
|
|
|
+ """管理浏览器实例的生命周期"""
|
|
|
+ def __init__(self, config: BrowserConfig = BrowserConfig()):
|
|
|
+ self.config = config
|
|
|
+ self.browser:Browser = None
|
|
|
+ self.page:Page = None
|
|
|
+ self.page_ops: PageOperations = None
|
|
|
+
|
|
|
+ async def __aenter__(self):
|
|
|
+ """异步上下文管理器入口"""
|
|
|
+ self.browser = await AsyncCamoufox(
|
|
|
+ headless=self.config.headless,
|
|
|
+ geoip=self.config.geoip,
|
|
|
+ proxy=self.config.proxy
|
|
|
+ ).__aenter__()
|
|
|
+ # 自动创建新页面并导航到初始URL
|
|
|
+ self.page = await self.browser.new_page()
|
|
|
+ await self.page.goto(self.config.init_url)
|
|
|
+ self.page_ops = PageOperations(self.page, self.config)
|
|
|
+ logging.info(f"Browser initialized with page: {self.page.url}")
|
|
|
+ return self
|
|
|
+
|
|
|
+ async def __aexit__(self, exc_type, exc, tb):
|
|
|
+ """异步上下文管理器退出"""
|
|
|
+ await self.browser.__aexit__(exc_type, exc, tb)
|
|
|
+
|
|
|
+async def aio_main(config: BrowserConfig = BrowserConfig(init_url="https://www.google.com")):
|
|
|
+ """新版主运行函数(测试自动页面创建)"""
|
|
|
+ async with CamoufoxManager(config) as manager:
|
|
|
+ logging.info(f"当前页面URL: {manager.page.url}")
|
|
|
+
|
|
|
+ # 保持页面存活直到手动关闭
|
|
|
+ try:
|
|
|
+ while not manager.page.is_closed():
|
|
|
+ await asyncio.sleep(1)
|
|
|
+ except KeyboardInterrupt:
|
|
|
+ logging.info("接收到终止信号,关闭浏览器...")
|
|
|
+ finally:
|
|
|
+ await manager.page.close()
|
|
|
+ logging.info("测试完成")
|
|
|
+
|
|
|
+
|
|
|
+async def task():
|
|
|
+ pass
|
|
|
+def main():
|
|
|
+ asyncio.run(aio_main())
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|