主要功能: - 多用户管理系统 - 浏览器自动化(Playwright) - 任务编排和执行 - Docker容器化部署 - 数据持久化和日志管理 技术栈: - Flask 3.0.0 - Playwright 1.40.0 - SQLite with connection pooling - Docker + Docker Compose 部署说明详见README.md
215 lines
7.1 KiB
Python
Executable File
215 lines
7.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
浏览器自动下载安装模块
|
||
检测本地是否有Playwright浏览器,如果没有则自动下载安装
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
import shutil
|
||
import subprocess
|
||
from pathlib import Path
|
||
|
||
# 设置浏览器安装路径(支持Docker和本地环境)
|
||
# Docker环境: PLAYWRIGHT_BROWSERS_PATH环境变量已设置为 /ms-playwright
|
||
# 本地环境: 使用Playwright默认路径
|
||
if 'PLAYWRIGHT_BROWSERS_PATH' in os.environ:
|
||
BROWSERS_PATH = os.environ['PLAYWRIGHT_BROWSERS_PATH']
|
||
else:
|
||
# Windows: %USERPROFILE%\AppData\Local\ms-playwright
|
||
# Linux: ~/.cache/ms-playwright
|
||
if sys.platform == 'win32':
|
||
BROWSERS_PATH = str(Path.home() / "AppData" / "Local" / "ms-playwright")
|
||
else:
|
||
BROWSERS_PATH = str(Path.home() / ".cache" / "ms-playwright")
|
||
os.environ["PLAYWRIGHT_BROWSERS_PATH"] = BROWSERS_PATH
|
||
|
||
|
||
class BrowserInstaller:
|
||
"""浏览器安装器"""
|
||
|
||
def __init__(self, log_callback=None):
|
||
"""
|
||
初始化安装器
|
||
|
||
Args:
|
||
log_callback: 日志回调函数
|
||
"""
|
||
self.log_callback = log_callback
|
||
|
||
def log(self, message):
|
||
"""输出日志"""
|
||
if self.log_callback:
|
||
self.log_callback(message)
|
||
else:
|
||
try:
|
||
print(message)
|
||
except UnicodeEncodeError:
|
||
# 如果打印Unicode字符失败,替换特殊字符
|
||
safe_message = message.replace('✓', '[OK]').replace('✗', '[X]')
|
||
print(safe_message)
|
||
|
||
def check_playwright_installed(self):
|
||
"""检查Playwright是否已安装"""
|
||
try:
|
||
import playwright
|
||
self.log("✓ Playwright已安装")
|
||
return True
|
||
except ImportError:
|
||
self.log("✗ Playwright未安装")
|
||
return False
|
||
|
||
def check_chromium_installed(self):
|
||
"""检查Chromium浏览器是否已安装"""
|
||
try:
|
||
from playwright.sync_api import sync_playwright
|
||
|
||
# 尝试启动浏览器检查是否可用
|
||
with sync_playwright() as p:
|
||
try:
|
||
# 使用超时快速检查
|
||
browser = p.chromium.launch(headless=True, timeout=5000)
|
||
browser.close()
|
||
self.log("✓ Chromium浏览器已安装且可用")
|
||
return True
|
||
except Exception as e:
|
||
error_msg = str(e)
|
||
self.log(f"✗ Chromium浏览器不可用: {error_msg}")
|
||
|
||
# 检查是否是路径不存在的错误
|
||
if "Executable doesn't exist" in error_msg:
|
||
self.log("检测到浏览器文件缺失,需要重新安装")
|
||
|
||
return False
|
||
except Exception as e:
|
||
self.log(f"✗ 检查浏览器时出错: {str(e)}")
|
||
return False
|
||
|
||
def install_chromium(self):
|
||
"""安装Chromium浏览器"""
|
||
try:
|
||
self.log("正在安装 Chromium 浏览器...")
|
||
|
||
# 查找 playwright 可执行文件
|
||
playwright_cli = None
|
||
possible_paths = [
|
||
os.path.join(os.path.dirname(sys.executable), "Scripts", "playwright.exe"),
|
||
os.path.join(os.path.dirname(sys.executable), "playwright.exe"),
|
||
os.path.join(os.path.dirname(sys.executable), "Scripts", "playwright"),
|
||
os.path.join(os.path.dirname(sys.executable), "playwright"),
|
||
"playwright", # 系统PATH中
|
||
]
|
||
|
||
for path in possible_paths:
|
||
if os.path.exists(path) or shutil.which(path):
|
||
playwright_cli = path
|
||
break
|
||
|
||
# 如果找到了 playwright CLI,直接调用
|
||
if playwright_cli:
|
||
self.log(f"使用 Playwright CLI: {playwright_cli}")
|
||
result = subprocess.run(
|
||
[playwright_cli, "install", "chromium"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=300
|
||
)
|
||
else:
|
||
# 检测是否是 Nuitka 编译的程序
|
||
is_nuitka = hasattr(sys, 'frozen') or '__compiled__' in globals()
|
||
|
||
if is_nuitka:
|
||
self.log("检测到 Nuitka 编译环境")
|
||
self.log("✗ 无法找到 playwright CLI 工具")
|
||
self.log("请手动运行: playwright install chromium")
|
||
return False
|
||
else:
|
||
# 使用 python -m
|
||
result = subprocess.run(
|
||
[sys.executable, "-m", "playwright", "install", "chromium"],
|
||
capture_output=True,
|
||
text=True,
|
||
timeout=300
|
||
)
|
||
|
||
if result.returncode == 0:
|
||
self.log("✓ Chromium浏览器安装成功")
|
||
return True
|
||
else:
|
||
self.log(f"✗ 浏览器安装失败: {result.stderr}")
|
||
return False
|
||
|
||
except subprocess.TimeoutExpired:
|
||
self.log("✗ 浏览器安装超时")
|
||
return False
|
||
except Exception as e:
|
||
self.log(f"✗ 浏览器安装出错: {str(e)}")
|
||
return False
|
||
|
||
def auto_install(self):
|
||
"""
|
||
自动检测并安装所需环境
|
||
|
||
Returns:
|
||
是否成功安装或已安装
|
||
"""
|
||
self.log("=" * 60)
|
||
self.log("检查浏览器环境...")
|
||
self.log("=" * 60)
|
||
|
||
# 1. 检查Playwright是否安装
|
||
if not self.check_playwright_installed():
|
||
self.log("✗ Playwright未安装,无法继续")
|
||
self.log("请确保程序包含 Playwright 库")
|
||
return False
|
||
|
||
# 2. 检查Chromium浏览器是否安装
|
||
if not self.check_chromium_installed():
|
||
self.log("\n未检测到Chromium浏览器,开始自动安装...")
|
||
|
||
# 安装浏览器
|
||
if not self.install_chromium():
|
||
self.log("✗ 浏览器安装失败")
|
||
self.log("\n您可以尝试以下方法:")
|
||
self.log("1. 手动执行: playwright install chromium")
|
||
self.log("2. 检查网络连接后重试")
|
||
self.log("3. 检查防火墙设置")
|
||
return False
|
||
|
||
self.log("\n" + "=" * 60)
|
||
self.log("✓ 浏览器环境检查完成,一切就绪!")
|
||
self.log("=" * 60 + "\n")
|
||
|
||
return True
|
||
|
||
|
||
def check_and_install_browser(log_callback=None):
|
||
"""
|
||
便捷函数:检查并安装浏览器
|
||
|
||
Args:
|
||
log_callback: 日志回调函数
|
||
|
||
Returns:
|
||
是否成功
|
||
"""
|
||
installer = BrowserInstaller(log_callback)
|
||
return installer.auto_install()
|
||
|
||
|
||
# 测试代码
|
||
if __name__ == "__main__":
|
||
print("浏览器自动安装工具")
|
||
print("=" * 60)
|
||
|
||
installer = BrowserInstaller()
|
||
success = installer.auto_install()
|
||
|
||
if success:
|
||
print("\n✓ 安装成功!您现在可以运行主程序了。")
|
||
else:
|
||
print("\n✗ 安装失败,请查看上方错误信息。")
|
||
|
||
print("=" * 60)
|