Files
hot-news-api/app/services/browser_manager.py
2026-03-26 15:04:59 +08:00

121 lines
4.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import threading
import time
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from app.utils.logger import log
class BrowserManager:
"""浏览器管理器提供共享的Chrome浏览器实例"""
_instance = None
_lock = threading.Lock()
_driver = None
_driver_path = None
_last_activity = 0
_max_idle_time = 1800 # 最大空闲时间默认30分钟
def __new__(cls, *args, **kwargs):
"""单例模式实现"""
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super(BrowserManager, cls).__new__(cls)
cls._instance._init_driver_path()
cls._instance._start_idle_monitor()
return cls._instance
def _init_driver_path(self):
"""初始化ChromeDriver路径"""
try:
self._driver_path = ChromeDriverManager().install()
log.info(f"ChromeDriver已安装: {self._driver_path}")
except Exception as e:
log.error(f"ChromeDriver安装失败: {str(e)}")
raise
def _start_idle_monitor(self):
"""启动空闲监控线程"""
def monitor():
while True:
time.sleep(60) # 每分钟检查一次
try:
with self._lock:
if self._driver is not None:
current_time = time.time()
if current_time - self._last_activity > self._max_idle_time:
log.info(f"浏览器空闲超过{self._max_idle_time}秒,释放资源")
self._quit_driver()
except Exception as e:
log.error(f"浏览器监控线程异常: {str(e)}")
monitor_thread = threading.Thread(target=monitor, daemon=True)
monitor_thread.start()
log.info("浏览器空闲监控线程已启动")
def get_driver(self):
"""获取Chrome浏览器实例"""
with self._lock:
self._last_activity = time.time()
if self._driver is None:
self._create_driver()
return self._driver
def _create_driver(self):
"""创建新的Chrome浏览器实例"""
log.info("创建新的Chrome浏览器实例")
options = webdriver.ChromeOptions()
# 基本配置(无头模式)
options.add_argument("--headless")
options.add_argument("--disable-gpu")
options.add_argument("--no-sandbox")
# 内存优化配置
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--disable-extensions")
options.add_argument("--disable-application-cache")
options.add_argument("--js-flags=--expose-gc")
options.add_argument("--memory-pressure-off")
options.add_argument("--disable-default-apps")
# 日志级别
options.add_argument("--log-level=3")
self._driver = webdriver.Chrome(
service=Service(self._driver_path),
options=options
)
self._driver.set_page_load_timeout(30)
def _quit_driver(self):
"""关闭浏览器实例"""
if self._driver:
try:
self._driver.quit()
log.info("浏览器实例已关闭")
except Exception as e:
log.error(f"关闭浏览器实例出错: {str(e)}")
finally:
self._driver = None
def release_driver(self):
"""使用完毕后标记为活动状态"""
with self._lock:
self._last_activity = time.time()
def get_page_content(self, url, wait_time=5):
"""获取指定URL的页面内容并自动处理浏览器"""
driver = self.get_driver()
try:
driver.get(url)
time.sleep(wait_time) # 等待页面加载
page_source = driver.page_source
self.release_driver()
return page_source, driver
except Exception as e:
log.error(f"获取页面内容失败: {str(e)}")
self.release_driver()
raise
def shutdown(self):
"""关闭浏览器管理器"""
with self._lock:
self._quit_driver()