go語言使用Chromedp實現(xiàn)二維碼登陸教程示例源碼
1 Chromedp是什么
chromedp是一個更快、更簡單的Golang庫用于調(diào)用支持Chrome DevTools協(xié)議的瀏覽器,同時不需要額外的依賴(例如Selenium和PhantomJS)
Chrome和Golang都與Google有著相當(dāng)密切的關(guān)系,而Chrome DevTools其實就是Chrome瀏覽器按下F12之后的控制終端
2 為什么不使用Selenium
對于Golang開發(fā)來說,使用chromedp更為便捷,因為它僅僅需要Chrome瀏覽器而并不需要依賴ChromeDriver,省去了依賴問題,有助于自動化的構(gòu)建和多平臺架構(gòu)的遷移
3 文章解決了什么需求
- 如何使用chromedp進(jìn)行二維碼登陸
- 如何將二維碼展示在無圖形化的終端上(makiuchi-d/gozxing解碼skip2/ go-qrcode編碼)
- 如何保存Cookies實現(xiàn)短時間免登陸
網(wǎng)站會更新,文章不保證更新,請務(wù)必學(xué)會舉一反三
4.如何使用chromedp進(jìn)行二維碼登陸
4.1 安裝chromedp
- 下載并安裝Chrome瀏覽器
- 創(chuàng)建Golang項目,開啟Go Module(在項目目錄下使用終端輸入go mod init)
- 在項目目錄下使用終端輸入:
go get -u github.com/chromedp/chromedp
(如果有依賴問題請刪除-u)
4.2 嘗試打開網(wǎng)站
(以金山文檔https://account.wps.cn/為例)
1.重新設(shè)置chromedp使用"有頭"的方式打開,以便于我們進(jìn)行debug
func main(){ // chromdp依賴context上限傳遞參數(shù) ctx, _ := chromedp.NewExecAllocator( context.Background(), // 以默認(rèn)配置的數(shù)組為基礎(chǔ),覆寫headless參數(shù) // 當(dāng)然也可以根據(jù)自己的需要進(jìn)行修改,這個flag是瀏覽器的設(shè)置 append( chromedp.DefaultExecAllocatorOptions[:], chromedp.Flag("headless", false), )..., ) }
2.創(chuàng)建chromedp上下文對象
func main(){ // chromdp依賴context上限傳遞參數(shù) ... // 創(chuàng)建新的chromedp上下文對象,超時時間的設(shè)置不分先后 // 注意第二個返回的參數(shù)是cancel(),只是我省略了 ctx, _ = context.WithTimeout(ctx, 30*time.Second) ctx, _ = chromedp.NewContext( ctx, // 設(shè)置日志方法 chromedp.WithLogf(log.Printf), ) // 通??梢允褂?defer cancel() 去取消 // 但是在Windows環(huán)境下,我們希望程序能順帶關(guān)閉掉瀏覽器 // 如果不希望瀏覽器關(guān)閉,使用cancel()方法即可 // defer cancel() // defer chromedp.Cancel(ctx) }
3.執(zhí)行自定義的任務(wù)
func main(){ // chromdp依賴context上限傳遞參數(shù) ... // 創(chuàng)建新的chromedp上下文對象,超時時間的設(shè)置不分先后 // 注意第二個返回的參數(shù)是cancel(),只是我省略了 ... // 執(zhí)行我們自定義的任務(wù) - myTasks函數(shù)在第4步 if err := chromedp.Run(ctx, myTasks()); err != nil { log.Fatal(err) return } }
4.至此程序的初始化過程已經(jīng)完成,接下來就是任務(wù)——打開登陸頁面
// 自定義任務(wù) func myTasks() chromedp.Tasks { return chromedp.Tasks{ // 1. 打開金山文檔的登陸界面 chromedp.Navigate(loginURL), } }
5.運行一下程序,可以看到Chrome被打開,同時訪問了我們指定的網(wǎng)站
4.3 獲取二維碼(點擊過程)
1.需要點擊微信登陸按鈕,先找到按鈕的選擇器,右鍵按鈕并在菜單中點擊檢查,然后可以看到按鈕的元素
2.右鍵元素打開菜單找到copy下的copy selector,即獲取到選擇器
3.我們嘗試點擊微信登陸按鈕,發(fā)現(xiàn)還需要點擊一下確認(rèn),重復(fù)上述步驟獲取確認(rèn)按鈕的選擇器
4.用代碼執(zhí)行上述點擊步驟
// 自定義任務(wù) func myTasks() chromedp.Tasks { return chromedp.Tasks{ // 1. 打開金山文檔的登陸界面 chromedp.Navigate(loginURL), // 2. 點擊微信登陸按鈕 // #wechat > span:nth-child(2) chromedp.Click(`#wechat > span:nth-child(2)`), // 3. 點擊確認(rèn)按鈕 // #dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok chromedp.Click(`#dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok`), } }
5.運行程序即可直達(dá)二維碼展示界面
6.用同樣的方式,獲取二維碼圖片的選擇器
7.用代碼實現(xiàn)獲取二維碼
有兩點需要注意,第一是二維碼有加載過程,第二是二維碼是元素渲染,
我們需要用截圖的方式獲?。ㄒ部梢杂胘s來獲取對應(yīng)的href并下載,但是為了照顧小白,選擇最簡單的)
func myTasks() chromedp.Tasks { return chromedp.Tasks{ // 1. 打開金山文檔的登陸界面 ... // 2. 點擊微信登陸按鈕 ... // 3. 點擊確認(rèn)按鈕 ... // 4. 獲取二維碼 // #wximport getCode(), } } func getCode() chromedp.ActionFunc { return func(ctx context.Context) (err error) { // 1. 用于存儲圖片的字節(jié)切片 var code []byte // 2. 截圖 // 注意這里需要注明直接使用ID選擇器來獲取元素(chromedp.ByID) if err = chromedp.Screenshot(`#wximport`, &code, chromedp.ByID).Do(ctx); err != nil { return } // 3. 保存文件 if err = ioutil.WriteFile("code.png", code, 0755); err != nil { return } return } }
8.執(zhí)行程序即可發(fā)現(xiàn)目錄下已經(jīng)存儲了二維碼圖片文件,我們可以通過掃描此二維碼進(jìn)行登陸,與瀏覽器上掃描為同一種效果
5. 如何將二維碼展示在無圖形化的終端上
(與chromedp無關(guān),屬于額外內(nèi)容)
1.在上述步驟中,我們已經(jīng)獲取了二維碼,接下來我們需要在終端顯示二維碼,首先是解碼,這里使用gozxing庫
func printQRCode(code []byte) (err error) { // 1. 因為我們的字節(jié)流是圖像,所以我們需要先解碼字節(jié)流 img, _, err := image.Decode(bytes.NewReader(code)) if err != nil { return } // 2. 然后使用gozxing庫解碼圖片獲取二進(jìn)制位圖 bmp, err := gozxing.NewBinaryBitmapFromImage(img) if err != nil { return } // 3. 用二進(jìn)制位圖解碼獲取gozxing的二維碼對象 res, err := qrcode.NewQRCodeReader().Decode(bmp, nil) if err != nil { return } return }
2.然后重新編碼來輸出二維碼到終端,這里使用go-qrcode庫
// 請注意import的庫發(fā)生了重名 import ( "github.com/makiuchi-d/gozxing" "github.com/makiuchi-d/gozxing/qrcode" goQrcode "github.com/skip2/go-qrcode" ) func printQRCode(code []byte) (err error) { // 1. 因為我們的字節(jié)流是圖像,所以我們需要先解碼字節(jié)流 ... // 2. 然后使用gozxing庫解碼圖片獲取二進(jìn)制位圖 ... // 3. 用二進(jìn)制位圖解碼獲取gozxing的二維碼對象 ... // 4. 用結(jié)果來獲取go-qrcode對象(注意這里我用了庫的別名) qr, err := goQrcode.New(res.String(), goQrcode.High) if err != nil { return } // 5. 輸出到標(biāo)準(zhǔn)輸出流 fmt.Println(qr.ToSmallString(false)) return }
3.修改我們第二步的過程
func getCode() chromedp.ActionFunc { return func(ctx context.Context) (err error) { // 1. 用于存儲圖片的字節(jié)切片 ... // 2. 截圖 // 注意這里需要注明直接使用ID選擇器來獲取元素(chromedp.ByID) ... // 3. 把二維碼輸出到標(biāo)準(zhǔn)輸出流 if err = printQRCode(code); err != nil { return err } return } }
4.運行程序即可查看效果
6. 如何保存Cookies實現(xiàn)短時間免登陸
1.在上述過程中,我們可以通過二維碼掃描登陸,網(wǎng)站會在登陸之后進(jìn)行跳轉(zhuǎn),跳轉(zhuǎn)后我們需要保存cookies來維持我們的登錄狀態(tài),代碼實現(xiàn)如下
// 保存Cookies func saveCookies() chromedp.ActionFunc { return func(ctx context.Context) (err error) { // 等待二維碼登陸 if err = chromedp.WaitVisible(`#app`, chromedp.ByID).Do(ctx); err != nil { return } // cookies的獲取對應(yīng)是在devTools的network面板中 // 1. 獲取cookies cookies, err := network.GetAllCookies().Do(ctx) if err != nil { return } // 2. 序列化 cookiesData, err := network.GetAllCookiesReturns{Cookies: cookies}.MarshalJSON() if err != nil { return } // 3. 存儲到臨時文件 if err = ioutil.WriteFile("cookies.tmp", cookiesData, 0755); err != nil { return } return } }
2.獲取到Cookies之后,我們需要在程序運行時將Cookies從臨時文件中加載到瀏覽器中
// 加載Cookies func loadCookies() chromedp.ActionFunc { return func(ctx context.Context) (err error) { // 如果cookies臨時文件不存在則直接跳過 if _, _err := os.Stat("cookies.tmp"); os.IsNotExist(_err) { return } // 如果存在則讀取cookies的數(shù)據(jù) cookiesData, err := ioutil.ReadFile("cookies.tmp") if err != nil { return } // 反序列化 cookiesParams := network.SetCookiesParams{} if err = cookiesParams.UnmarshalJSON(cookiesData); err != nil { return } // 設(shè)置cookies return network.SetCookies(cookiesParams.Cookies).Do(ctx) } }
3.通過上述兩步我們已經(jīng)可以保持登陸狀態(tài),然后我們需要檢查一下是否成功,這里調(diào)用瀏覽器執(zhí)行js腳本獲取當(dāng)前頁面的網(wǎng)址,判斷是否已經(jīng)個人中心頁面,如果為真,則停止操作
// 檢查是否登陸 func checkLoginStatus() chromedp.ActionFunc { return func(ctx context.Context) (err error) { var url string if err = chromedp.Evaluate(`window.location.href`, &url).Do(ctx); err != nil { return } if strings.Contains(url, "https://account.wps.cn/usercenter/apps") { log.Println("已經(jīng)使用cookies登陸") chromedp.Stop() } return } }
4.最終重新設(shè)置我們的瀏覽器任務(wù)即可
// 自定義任務(wù) func myTasks() chromedp.Tasks { return chromedp.Tasks{ // 0. 加載cookies <-- 變動 loadCookies(), // 1. 打開金山文檔的登陸界面 ... // 判斷一下是否已經(jīng)登陸 <-- 變動 checkLoginStatus(), // 2. 點擊微信登陸按鈕 // #wechat > span:nth-child(2) ... // 3. 點擊確認(rèn)按鈕 // #dialog > div.dialog-wrapper > div > div.dialog-footer > div.dialog-footer-ok ... // 4. 獲取二維碼 // #wximport ... // 5. 若二維碼登錄后,瀏覽器會自動跳轉(zhuǎn)到用戶信息頁面 <-- 變動 saveCookies(), } }
5.我們使用已經(jīng)登陸的cookies運行程序可以發(fā)現(xiàn)我們成功跳過登陸過程
以上就是go語言使用Chromedp實現(xiàn)二維碼登陸教程示例源碼的詳細(xì)內(nèi)容,更多關(guān)于go Chromedp二維碼登陸的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
搭建Go語言的ORM框架Gorm的具體步驟(從Java到go)
很多朋友不知道如何使用Goland軟件,搭建一個ORM框架GORM,今天小編給大家分享一篇教程關(guān)于搭建Go語言的ORM框架Gorm的具體步驟(從Java到go),感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2022-09-09Go語言異常處理(Panic和recovering)用法詳解
異常處理是程序健壯性的關(guān)鍵,往往開發(fā)人員的開發(fā)經(jīng)驗的多少從異常部分處理上就能得到體現(xiàn)。Go語言中沒有Try?Catch?Exception機(jī)制,但是提供了panic-and-recover機(jī)制,本文就來詳細(xì)講講他們的用法2022-07-07Golang 如何判斷數(shù)組某個元素是否存在 (isset)
這篇文章主要介紹了Golang 如何判斷數(shù)組某個元素是否存在 (isset),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04