add:增加配置”是否使用动态ua池“

This commit is contained in:
ddCat
2025-03-27 15:56:42 +08:00
parent 0b61d96f2b
commit 70d7872156
5 changed files with 180 additions and 95 deletions

View File

@@ -4,12 +4,17 @@ BROWSER_HEADLESS=True
BROWSER_USER_AGENT="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
# 浏览器代理
BROWSER_PROXY=
# 是否使用动态ua池
DYNAMIC_USERAGENT=False
# Windows 浏览器路径
#BROWSER_PATH="C:\Users\Administrator\AppData\Local\Google\Chrome\Bin\chrome.exe"
# 多个域名使用逗号分隔
EMAIL_DOMAINS=xxx.xxx
EMAIL_DOMAINS=xxx.xx
# 临时邮箱用户名
EMAIL_USERNAME=xxxx
EMAIL_USERNAME=xxx
# 临时邮箱PIN码如果需要
EMAIL_PIN=
@@ -33,9 +38,6 @@ API_WORKERS=1
# 是否启用UI
ENABLE_UI=True
# Windows 浏览器路径
#BROWSER_PATH="C:\Users\Administrator\AppData\Local\Google\Chrome\Bin\chrome.exe"
# Cursor main.js 主文件路径
# windows用户部分安装时是自定义目录安装的需要修改该配置
#CURSOR_PATH="D:\devtools\cursor"

5
api.py
View File

@@ -975,6 +975,7 @@ async def use_account_token(id: int):
# 添加配置相关模型
class ConfigModel(BaseModel):
BROWSER_HEADLESS: bool
DYNAMIC_USERAGENT: Optional[bool] = False
BROWSER_USER_AGENT: str
MAX_ACCOUNTS: int
EMAIL_DOMAINS: str
@@ -999,7 +1000,8 @@ async def get_config():
"EMAIL_USERNAME": os.getenv("EMAIL_USERNAME", ""),
"EMAIL_PIN": os.getenv("EMAIL_PIN", ""),
"BROWSER_PATH": os.getenv("BROWSER_PATH", ""),
"CURSOR_PATH": os.getenv("CURSOR_PATH", "")
"CURSOR_PATH": os.getenv("CURSOR_PATH", ""),
"DYNAMIC_USERAGENT": os.getenv("DYNAMIC_USERAGENT", "False").lower() == "true"
}
return {"success": True, "data": config}
@@ -1025,6 +1027,7 @@ async def update_config(config: ConfigModel):
# 构建配置字典
config_dict = {
"BROWSER_HEADLESS": str(config.BROWSER_HEADLESS),
"DYNAMIC_USERAGENT": str(config.DYNAMIC_USERAGENT),
"BROWSER_USER_AGENT": config.BROWSER_USER_AGENT,
"MAX_ACCOUNTS": str(config.MAX_ACCOUNTS),
"EMAIL_DOMAINS": config.EMAIL_DOMAINS,

View File

@@ -1,104 +1,143 @@
from DrissionPage import ChromiumOptions, Chromium
import sys
import os
from dotenv import load_dotenv
from logger import info, warning
from logger import info, warning, error
from config import (
BROWSER_USER_AGENT,
BROWSER_PATH,
BROWSER_HEADLESS,
BROWSER_PROXY,
DYNAMIC_USERAGENT
)
import random
import time
load_dotenv()
# 用户代理列表 - 按平台分类
USER_AGENTS = {
'windows': [
# Chrome Windows
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
# Edge Windows
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.76',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.47',
# Firefox Windows
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
],
'macos': [
# Chrome macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
# Safari macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15',
# Firefox macOS
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/118.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0',
],
'linux': [
# Chrome Linux
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
# Firefox Linux
'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0',
]
}
def get_random_user_agent():
"""根据当前平台获取随机用户代理"""
platform = 'windows'
if sys.platform == 'darwin':
platform = 'macos'
elif sys.platform.startswith('linux'):
platform = 'linux'
# 获取当前平台的UA列表
ua_list = USER_AGENTS.get(platform, USER_AGENTS['windows'])
return random.choice(ua_list)
class BrowserManager:
def __init__(self):
self.browser = None
def init_browser(self):
"""初始化浏览器"""
co = self._get_browser_options()
self.browser = Chromium(co)
return self.browser
def _get_browser_options(self):
"""获取浏览器配置"""
co = ChromiumOptions()
# 设置浏览器路径 - 添加这段代码
browser_path = os.getenv("BROWSER_PATH")
if not browser_path and sys.platform == "win32":
# 仅在Windows系统下尝试常见的浏览器路径
possible_paths = [
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
r"C:\Program Files\Microsoft\Edge\Application\msedge.exe",
r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
]
for path in possible_paths:
if os.path.exists(path):
browser_path = path
info(f"自动找到浏览器路径: {browser_path}")
break
if not browser_path:
info("未找到浏览器路径,请在.env文件中设置BROWSER_PATH环境变量")
if browser_path:
co.set_browser_path(browser_path)
try:
extension_path = self._get_extension_path()
co.add_extension(extension_path)
except FileNotFoundError as e:
info(f"警告: {e}")
info("正在初始化浏览器...")
co = ChromiumOptions()
co.set_user_agent(
os.getenv(
"BROWSER_USER_AGENT",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36",
if BROWSER_PROXY:
co.set_proxy(BROWSER_PROXY)
# 如果配置了特定的浏览器路径,则使用
if BROWSER_PATH and os.path.exists(BROWSER_PATH):
co.set_browser_path(BROWSER_PATH)
info(f"使用自定义浏览器路径: {BROWSER_PATH}")
try:
extension_path = self._get_extension_path()
co.add_extension(extension_path)
except FileNotFoundError as e:
info(f"警告: {e}")
# 设置User-Agent
if DYNAMIC_USERAGENT:
# 随机选择一个User-Agent
user_agent = get_random_user_agent()
info(f"使用动态User-Agent: {user_agent}")
co.set_user_agent(user_agent)
else:
info(f"使用固定User-Agent: {BROWSER_USER_AGENT}")
co.set_user_agent(BROWSER_USER_AGENT)
co.set_pref("credentials_enable_service", False)
co.set_argument("--hide-crash-restore-bubble")
# 禁用自动化特征(关键参数)
co.set_argument("--disable-blink-features=AutomationControlled")
co.set_argument("--disable-features=AutomationControlled")
co.set_argument("--disable-automation-extension")
# 随机化指纹参数
co.set_pref("webgl.vendor", "NVIDIA Corporation")
co.set_pref(
"webgl.renderer",
"ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0)",
)
)
co.set_pref("credentials_enable_service", False)
co.set_argument("--hide-crash-restore-bubble")
proxy = os.getenv("BROWSER_PROXY")
if proxy:
co.set_proxy(proxy)
co.set_pref("navigator.plugins.length", 5)
co.set_pref("navigator.hardwareConcurrency", 8)
# 禁用自动化特征(关键参数
co.set_argument("--disable-blink-features=AutomationControlled")
co.set_argument("--disable-features=AutomationControlled")
co.set_argument("--disable-automation-extension")
# 覆盖自动化特征(关键)
co.set_pref("dom.webdriver.enabled", False)
co.set_pref("useAutomationExtension", False)
# 随机化指纹参数
co.set_pref("webgl.vendor", "NVIDIA Corporation")
co.set_pref(
"webgl.renderer",
"ANGLE (NVIDIA, NVIDIA GeForce RTX 3070 Direct3D11 vs_5_0 ps_5_0)",
)
co.set_pref("navigator.plugins.length", 5)
co.set_pref("navigator.hardwareConcurrency", 8)
# 设置时区参数
co.set_argument("--timezone=Asia/Shanghai")
co.set_pref("timezone.override", "Asia/Shanghai")
# 覆盖自动化特征(关键)
co.set_pref("dom.webdriver.enabled", False)
co.set_pref("useAutomationExtension", False)
# 设置更真实的屏幕参数
co.set_pref("screen.width", 1920)
co.set_pref("screen.height", 1080)
co.set_pref("screen.pixelDepth", 24)
co.auto_port()
co.headless(BROWSER_HEADLESS) # 生产环境使用无头模式
# 设置时区参数
co.set_argument("--timezone=Asia/Shanghai")
co.set_pref("timezone.override", "Asia/Shanghai")
# 设置更真实的屏幕参数
co.set_pref("screen.width", 1920)
co.set_pref("screen.height", 1080)
co.set_pref("screen.pixelDepth", 24)
co.auto_port()
co.headless(
os.getenv("BROWSER_HEADLESS", "True").lower() == "true"
) # 生产环境使用无头模式
# Mac 系统特殊处理
if sys.platform == "darwin" or sys.platform == "linux":
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
return co
# Mac 系统特殊处理
if sys.platform == "darwin" or sys.platform == "linux":
co.set_argument("--no-sandbox")
co.set_argument("--disable-gpu")
self.browser = Chromium(co)
info("浏览器初始化成功")
except Exception as e:
error(f"浏览器初始化失败: {str(e)}")
return self.browser
def _get_extension_path(self):
"""获取插件路径"""
root_dir = os.getcwd()
@@ -113,9 +152,10 @@ class BrowserManager:
return extension_path
def quit(self):
"""关闭浏览器"""
if self.browser:
try:
info("正在关闭浏览器...")
try:
if self.browser:
self.browser.quit()
except:
pass
info("浏览器已关闭")
except Exception as e:
error(f"关闭浏览器出错: {str(e)}")

View File

@@ -37,9 +37,15 @@ REGISTRATION_RETRY_INTERVAL = int(os.getenv("REGISTRATION_RETRY_INTERVAL", 5))
# 是否以无头模式运行浏览器(不显示界面)
BROWSER_HEADLESS = os.getenv("BROWSER_HEADLESS", "true").lower() == "true"
# 浏览器可执行文件路径(为空则使用默认路径)
BROWSER_EXECUTABLE_PATH = os.getenv("BROWSER_EXECUTABLE_PATH", None)
BROWSER_PATH = os.getenv("BROWSER_PATH", None)
# 浏览器下载文件保存路径
BROWSER_DOWNLOAD_PATH = os.getenv("BROWSER_DOWNLOAD_PATH", None)
# 是否使用动态ua池
DYNAMIC_USERAGENT = os.getenv("DYNAMIC_USERAGENT", "false").lower() == "true"
# 浏览器代理
BROWSER_PROXY = os.getenv("BROWSER_PROXY", None)
# 浏览器User-Agent
BROWSER_USER_AGENT = os.getenv("BROWSER_USER_AGENT", None)
# ===== Cursor URL配置 =====
# Cursor登录页面URL

View File

@@ -629,6 +629,14 @@
<small class="text-muted">用于注册的邮箱域名,多个域名用逗号分隔</small>
</div>
<div class="mb-3">
<label for="dynamic-useragent" class="form-label">动态User-Agent</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="dynamic-useragent" disabled>
<label class="form-check-label" for="dynamic-useragent">启用动态User-Agent</label>
</div>
<small class="text-muted">启用后将从预设的UA列表中随机选择无需手动配置User-Agent</small>
</div>
<div class="mb-3" id="useragent-input-container">
<label for="browser-useragent" class="form-label">浏览器User-Agent</label>
<input type="text" id="browser-useragent" class="form-control" disabled>
<small class="text-muted">浏览器模拟的用户代理字符串</small>
@@ -1828,6 +1836,11 @@
if (data.success) {
const config = data.data;
$("#browser-headless").val(config.BROWSER_HEADLESS.toString());
$("#dynamic-useragent").prop('checked', config.DYNAMIC_USERAGENT || false);
// 触发动态UA的change事件
$("#dynamic-useragent").trigger('change');
$("#browser-useragent").val(config.BROWSER_USER_AGENT);
$("#max-accounts").val(config.MAX_ACCOUNTS);
$("#email-domains").val(config.EMAIL_DOMAINS);
@@ -1849,9 +1862,12 @@
// 保存配置函数
function saveConfig() {
showLoading();
const isDynamicUA = $("#dynamic-useragent").prop('checked');
const config = {
BROWSER_HEADLESS: $("#browser-headless").val() === 'true',
BROWSER_USER_AGENT: $("#browser-useragent").val(),
DYNAMIC_USERAGENT: isDynamicUA,
BROWSER_USER_AGENT: isDynamicUA ? "" : $("#browser-useragent").val(),
MAX_ACCOUNTS: parseInt($("#max-accounts").val()),
EMAIL_DOMAINS: $("#email-domains").val(),
EMAIL_USERNAME: $("#email-username").val(),
@@ -1889,6 +1905,10 @@
const inputs = $('#config-form select, #config-form input');
if (enable) {
inputs.prop('disabled', false);
// 如果动态UA已启用保持UA输入框禁用
if ($("#dynamic-useragent").prop('checked')) {
$("#browser-useragent").prop('disabled', true);
}
$('#save-config-btn, #cancel-config-btn').show();
$('#edit-config-btn').hide();
} else {
@@ -1897,6 +1917,20 @@
$('#edit-config-btn').show();
}
}
// 动态User-Agent切换逻辑
$("#dynamic-useragent").change(function() {
const isDynamicUA = $(this).prop('checked');
if (isDynamicUA) {
$("#browser-useragent").prop('disabled', true);
$("#useragent-input-container").addClass('text-muted');
} else {
// 只有在编辑模式下才启用输入框
const isEditMode = !$("#edit-config-btn").is(":visible");
$("#browser-useragent").prop('disabled', !isEditMode);
$("#useragent-input-container").removeClass('text-muted');
}
});
</script>
<!-- 添加删除确认对话框 -->