rodHelper.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package rod_helper
  2. import (
  3. "context"
  4. _ "embed"
  5. "errors"
  6. "github.com/allanpk716/ChineseSubFinder/internal/pkg/log_helper"
  7. "github.com/allanpk716/ChineseSubFinder/internal/pkg/my_util"
  8. "github.com/allanpk716/ChineseSubFinder/internal/pkg/random_useragent"
  9. "github.com/go-rod/rod"
  10. "github.com/go-rod/rod/lib/launcher"
  11. "github.com/go-rod/rod/lib/proto"
  12. "github.com/mholt/archiver/v3"
  13. "os"
  14. "path/filepath"
  15. "sync"
  16. "time"
  17. )
  18. /**
  19. * @Description: 新建一个支持代理的 browser 对象,使用完毕后,需要删除 adblockFilePath 文件夹
  20. * @param httpProxyURL http://127.0.0.1:10809
  21. * @return *rod.Browser
  22. * @return error
  23. */
  24. func NewBrowser(httpProxyURL string, loadAdblock bool) (*rod.Browser, error) {
  25. var err error
  26. once.Do(func() {
  27. adblockSavePath, err = releaseAdblock()
  28. if err != nil {
  29. log_helper.GetLogger().Errorln("releaseAdblock", err)
  30. }
  31. })
  32. var browser *rod.Browser
  33. err = rod.Try(func() {
  34. purl := ""
  35. if loadAdblock == true {
  36. purl = launcher.New().
  37. Delete("disable-extensions").
  38. Set("load-extension", adblockSavePath).
  39. Proxy(httpProxyURL).
  40. Headless(false). // 插件模式需要设置这个
  41. //XVFB("--server-num=5", "--server-args=-screen 0 1600x900x16").
  42. //XVFB("-ac :99", "-screen 0 1280x1024x16").
  43. MustLaunch()
  44. } else {
  45. purl = launcher.New().
  46. Proxy(httpProxyURL).
  47. MustLaunch()
  48. }
  49. browser = rod.New().ControlURL(purl).MustConnect()
  50. })
  51. if err != nil {
  52. return nil, err
  53. }
  54. return browser, nil
  55. }
  56. /**
  57. * @Description: 访问目标 Url,返回 page,只是这个 page 有效,如果再次出发其他的事件无效
  58. * @param desURL 目标 Url
  59. * @param httpProxyURL http://127.0.0.1:10809
  60. * @param timeOut 超时时间
  61. * @param maxRetryTimes 当是非超时 err 的时候,最多可以重试几次
  62. * @return *rod.Page
  63. * @return error
  64. */
  65. func NewBrowserFromDocker(httpProxyURL, remoteDockerURL string) (*rod.Browser, error) {
  66. var browser *rod.Browser
  67. err := rod.Try(func() {
  68. l := launcher.MustNewManaged(remoteDockerURL)
  69. u := l.Proxy(httpProxyURL).MustLaunch()
  70. l.Headless(false).XVFB()
  71. browser = rod.New().Client(l.Client()).ControlURL(u).MustConnect()
  72. })
  73. if err != nil {
  74. return nil, err
  75. }
  76. return browser, nil
  77. }
  78. func NewPageNavigate(browser *rod.Browser, desURL string, timeOut time.Duration, maxRetryTimes int) (*rod.Page, error) {
  79. page, err := newPage(browser)
  80. if err != nil {
  81. return nil, err
  82. }
  83. err = page.SetUserAgent(&proto.NetworkSetUserAgentOverride{
  84. UserAgent: random_useragent.RandomUserAgent(true),
  85. })
  86. if err != nil {
  87. return nil, err
  88. }
  89. page = page.Timeout(timeOut)
  90. nowRetryTimes := 0
  91. for nowRetryTimes <= maxRetryTimes {
  92. err = rod.Try(func() {
  93. page.MustNavigate(desURL).MustWaitLoad()
  94. nowRetryTimes++
  95. })
  96. if errors.Is(err, context.DeadlineExceeded) {
  97. // 超时
  98. return nil, err
  99. } else if err == nil {
  100. // 没有问题
  101. return page, nil
  102. }
  103. }
  104. return nil, err
  105. }
  106. // ReloadBrowser 提前把浏览器下载好
  107. func ReloadBrowser() {
  108. newBrowser, err := NewBrowser("", true)
  109. if err != nil {
  110. return
  111. }
  112. defer func() {
  113. _ = newBrowser.Close()
  114. }()
  115. page, err := NewPageNavigate(newBrowser, "https://www.baidu.com", 30*time.Second, 5)
  116. if err != nil {
  117. return
  118. }
  119. defer func() {
  120. _ = page.Close()
  121. }()
  122. }
  123. // Clear 清理缓存
  124. func Clear() {
  125. _ = rod.Try(func() {
  126. l := launcher.New().
  127. Headless(false).
  128. Devtools(true)
  129. defer l.Cleanup() // remove launcher.FlagUserDataDir
  130. url := l.MustLaunch()
  131. // Trace shows verbose debug information for each action executed
  132. // Slowmotion is a debug related function that waits 2 seconds between
  133. // each action, making it easier to inspect what your code is doing.
  134. browser := rod.New().
  135. ControlURL(url).
  136. Trace(true).
  137. SlowMotion(2 * time.Second).
  138. MustConnect()
  139. defer browser.MustClose()
  140. })
  141. }
  142. func newPage(browser *rod.Browser) (*rod.Page, error) {
  143. page, err := browser.Page(proto.TargetCreateTarget{URL: ""})
  144. if err != nil {
  145. return nil, err
  146. }
  147. return page, err
  148. }
  149. // releaseAdblock 从程序中释放 adblock 插件出来到本地路径
  150. func releaseAdblock() (string, error) {
  151. adblockFolderPath := filepath.Join(os.TempDir(), "chinesesubfinder")
  152. err := os.MkdirAll(filepath.Join(adblockFolderPath), os.ModePerm)
  153. if err != nil {
  154. return "", err
  155. }
  156. desPath := filepath.Join(adblockFolderPath, "RunAdblock")
  157. // 清理之前缓存的信息
  158. _ = my_util.ClearFolder(desPath)
  159. // 具体把 adblock zip 解压下载到哪里
  160. outZipFileFPath := filepath.Join(adblockFolderPath, "adblock.zip")
  161. adblockZipFile, err := os.Create(outZipFileFPath)
  162. if err != nil {
  163. return "", err
  164. }
  165. defer func() {
  166. _ = adblockZipFile.Close()
  167. _ = os.Remove(outZipFileFPath)
  168. }()
  169. _, err = adblockZipFile.Write(adblockFolder)
  170. if err != nil {
  171. return "", err
  172. }
  173. r := archiver.NewZip()
  174. err = r.Unarchive(outZipFileFPath, desPath)
  175. if err != nil {
  176. return "", err
  177. }
  178. return filepath.Join(desPath, adblockInsideName), err
  179. }
  180. const adblockInsideName = "adblock"
  181. var once sync.Once
  182. // 这个文件内有一个子文件夹 adblock ,制作的时候务必注意
  183. //go:embed assets/adblock_v4.34.0.zip
  184. var adblockFolder []byte
  185. var adblockSavePath string