第一版

This commit is contained in:
zyj
2026-02-09 16:05:56 +08:00
parent fcdc5b8f2d
commit 514d7f9b0e
11 changed files with 1137 additions and 365 deletions

View File

@@ -1,13 +1,16 @@
package libs
import (
"log"
"sync"
"github.com/go-rod/rod"
)
// Browser 浏览器实例管理器
type Browser struct{}
// BrowserInfo 浏览器实例信息
type BrowserInfo struct {
Id int
Browser *rod.Browser
@@ -15,7 +18,7 @@ type BrowserInfo struct {
Page *rod.Page
}
// 浏览器指纹数据
// Fingerprint 浏览器指纹配置
type Fingerprint struct {
Viewport []struct {
Height int
@@ -26,58 +29,61 @@ type Fingerprint struct {
}
var (
BrowserPool = make(map[int]*BrowserInfo) // 初始化map
poolMutex sync.RWMutex // 添加读写锁保证并发安全
browserPool = make(map[int]*BrowserInfo)
poolMutex sync.RWMutex
)
var ChromeExample Chrome
// GetBrowser 获取或创建浏览器实例(线程安全,双重检查锁定)
func (*Browser) GetBrowser(id int, params *Fingerprint) *BrowserInfo {
poolMutex.RLock()
value, ok := BrowserPool[id]
value, ok := browserPool[id]
poolMutex.RUnlock()
if ok {
return value
}
// 未找到时获取写锁(互斥)
poolMutex.Lock()
defer poolMutex.Unlock()
// 双检查避免在等待锁期间其他goroutine已创建
if value, ok := BrowserPool[id]; ok {
// 双检查
if value, ok := browserPool[id]; ok {
return value
}
// 创建新实例补充CancelFunc
var ChromeExample Chrome
browser, url, page := ChromeExample.Create(id, params)
var chrome Chrome
browser, url, page := chrome.Create(id, params)
browserInfo := &BrowserInfo{
Id: id,
Browser: browser,
Url: url,
Page: page,
}
BrowserPool[id] = browserInfo
browserPool[id] = browserInfo
log.Printf("[浏览器] 创建实例 #%d", id)
return browserInfo
}
func (*Browser) GetAllBrowser() map[int]*BrowserInfo {
return BrowserPool
}
func (*Browser) CancelAllBrowser() {
for _, v := range BrowserPool {
// CloseAll 关闭所有浏览器实例
func (*Browser) CloseAll() {
poolMutex.Lock()
defer poolMutex.Unlock()
for id, v := range browserPool {
v.Browser.MustClose()
delete(browserPool, id)
}
log.Println("[浏览器] 已关闭所有实例")
}
func (*Browser) CancelBrowser(id int) {
browserInfo := BrowserPool[id]
if browserInfo == nil {
// Close 关闭指定浏览器实例
func (*Browser) Close(id int) {
poolMutex.Lock()
defer poolMutex.Unlock()
info, ok := browserPool[id]
if !ok {
return
}
browserInfo.Browser.MustClose()
delete(BrowserPool, id)
info.Browser.MustClose()
delete(browserPool, id)
log.Printf("[浏览器] 已关闭实例 #%d", id)
}

View File

@@ -11,70 +11,60 @@ import (
"github.com/go-rod/stealth"
)
// Chrome 浏览器创建器
type Chrome struct{}
// Create 创建一个带反检测的 Chrome 浏览器实例
func (c *Chrome) Create(accountId int, params *Fingerprint) (*rod.Browser, string, *rod.Page) {
var accountIdStr = strconv.Itoa(accountId)
userDataDir := "user-data/" + accountIdStr
userDataDir := "user-data/" + strconv.Itoa(accountId)
absPath, _ := filepath.Abs(userDataDir)
log.Printf("用户数据目录: %s", absPath)
path, _ := launcher.LookPath() // 获取当前系统的浏览器目录
// 判断是否传递代理
// var proxyHandle utils.ProxyHandle
// protocol, username, password, ip, port, errProxy := proxyHandle.ParseProxy(params.Proxy)
log.Printf("[Chrome] 用户数据目录: %s", absPath)
chromePath, _ := launcher.LookPath()
l := launcher.New().
Bin(path).
// Proxy("210.51.27.121:50003"). // 直接传入完整认证URL
Delete("use-mock-keychain"). // delete flag "--use-mock-keychain"
Set("disable-blink-features", "AutomationControlled"). // 绕过自动化检测
// Set("incognito"). // 无痕模式
Set("user-data-dir", absPath). // 数据持久化目录
Set("window-size", "1920,1480"). // 窗口尺寸
Set("disable-infobars", "true"). // 隐藏自动化提示栏
Set("no-sandbox", "true").
Bin(chromePath).
Delete("use-mock-keychain").
// 反自动化检测
Set("disable-blink-features", "AutomationControlled").
Set("excludeSwitches", "enable-automation").
Set("enable-gpu"). // 启用 GPU 加速
Set("ignore-certificate-errors"). // 忽略证书错误
Set("use-fake-ui-for-media-stream"). // 允许媒体流
Set("autoplay-policy", "no-user-gesture-required"). // 自动播放
Set("disable-infobars", "true").
// 数据持久化 & 窗口
Set("user-data-dir", absPath).
Set("window-size", "1920,1480").
// 性能 & 安全
Set("no-sandbox", "true").
Set("enable-gpu").
Set("ignore-certificate-errors").
Set("disable-application-cache").
Set("disable-dev-shm-usage").
// 禁用webrtc
Set("disable-webrtc", "true"). // 核心禁用参数
Set("disable-features", "WebRtcHideLocalIpsWithMdns"). // 隐藏本地IP
Set("webrtc-ip-handling-policy", "disable_non_proxied_udp"). // 禁用非代理UDP
// 媒体
Set("use-fake-ui-for-media-stream").
Set("autoplay-policy", "no-user-gesture-required").
// 隐私: 禁用 WebRTC 防止 IP 泄露
Set("disable-webrtc", "true").
Set("disable-features", "WebRtcHideLocalIpsWithMdns").
Set("webrtc-ip-handling-policy", "disable_non_proxied_udp").
Set("force-webrtc-ip-handling-policy").
Headless(false) // 关闭无头模式
// 修改时区
l.Set("disable-geolocation", "true") // 启用地理定位API
l.Set("disable-web-security", "true") // 允许跨域(某些网站需要) 可以跨域访问iframe标签
l.Set("disable-notifications", "true")
l.Set("disable-site-isolation-trials", "true")
// if errProxy == nil {
// if strings.Contains(protocol, "http") {
// log.Println(protocol + "://" + ip + ":" + port)
// l = l.Proxy(ip + ":" + port)
// }
// }
// 跨域 & 通知
Set("disable-web-security", "true").
Set("disable-notifications", "true").
Set("disable-site-isolation-trials", "true").
Set("disable-geolocation", "true").
Headless(false)
uri := l.MustLaunch()
browser := rod.New().
ControlURL(uri).
MustConnect().NoDefaultDevice()
MustConnect().
NoDefaultDevice()
browser.MustIgnoreCertErrors(true)
// if errProxy == nil {
// if strings.Contains(protocol, "http") {
// log.Println(username, password)
// go browser.MustHandleAuth(username, password)()
// }
// }
page := stealth.MustPage(browser)
// page := browser.MustPage()
// 使用 stealth 模式创建页面
page := stealth.MustPage(browser)
// 为所有新打开的页面也注入 stealth 脚本
go browser.EachEvent(func(e *proto.TargetTargetCreated) {
if e.TargetInfo.Type != proto.TargetTargetInfoTypePage {
return
@@ -82,8 +72,8 @@ func (c *Chrome) Create(accountId int, params *Fingerprint) (*rod.Browser, strin
browser.MustPageFromTargetID(e.TargetInfo.TargetID).MustEvalOnNewDocument(stealth.JS)
})()
// 在页面加载前注入时区覆盖代码
page.MustSetViewport(1920, 1480, 1.0, false)
log.Printf("[Chrome] 浏览器已启动 (账号 #%d)", accountId)
return browser, uri, page
}