rodHelper.go 5.8 KB

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