1. 程式人生 > 其它 >Golang 百度雲掃碼登入

Golang 百度雲掃碼登入

技術標籤:Go

檔案結構:

在這裡插入圖片描述

http_client.go程式碼如下:

package client

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
	"time"
)


// HTTPClient http client
type HTTPClient struct {
*http.Client UserAgent string } var ( UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36" ) func NewHTTPClient() *HTTPClient { j,_ := cookiejar.New(nil) h := &HTTPClient{ Client: &http.Client{ Timeout:
30 * time.Second, Jar:j, }, UserAgent: UserAgent, } return h } // SetUserAgent 設定 UserAgent 瀏覽器標識 func (h *HTTPClient) SetUserAgent(ua string) { h.UserAgent = ua } // SetTimeout 設定 http 請求超時時間, 預設30s func (h *HTTPClient) SetTimeout(t time.Duration) { h.Client.Timeout = t } func(h *HTTPClient)
Fetch(webUrl string,method string,headers map[string]string,data interface{})(respBody []byte,err error){ var req *http.Request var obody io.Reader if data != nil { switch value := data.(type) { case io.Reader: obody = value case map[string]string: query := url.Values{} for k := range value { query.Set(k, value[k]) } obody = strings.NewReader(query.Encode()) case map[string]interface{}: query := url.Values{} for k := range value { query.Set(k, fmt.Sprint(value[k])) } obody = strings.NewReader(query.Encode()) case map[interface{}]interface{}: query := url.Values{} for k := range value { query.Set(fmt.Sprint(k), fmt.Sprint(value[k])) } obody = strings.NewReader(query.Encode()) case string: obody = strings.NewReader(value) case []byte: obody = bytes.NewReader(value[:]) default: return nil, fmt.Errorf("requester.Req: unknown post type: %s", value) } } req, err = http.NewRequest(method, webUrl, obody) if err!=nil{ return } if headers!=nil{ for k,v:=range headers{ req.Header.Set(k,v) } } if method==http.MethodPost{ req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } req.Header.Set("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36") resp,err:=h.Client.Do(req) if err!=nil{ return } defer resp.Body.Close() //fmt.Println("res Cookies is :",resp.Cookies()) //u,_:= url.Parse(webUrl) //h.Client.Jar.SetCookies(u,resp.Cookies()) //fmt.Println("cookie jar is:",h.Client.Jar.Cookies(u)) respBody,err=ioutil.ReadAll(resp.Body) return }

main.go程式碼如下:

package main

import (
	"baidu_qr/client"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/robertkrimen/otto"
)

type QrImageData struct {
	ImageUrl string `json:"imgurl"`
	Errno    int64  `json:"errno"`
	Sign     string `json:"sign"`
}

type QueryData struct {
	ChannelV string `json:"channel_v"`
}

type QrCodeLogin struct {
	httpClient *client.HTTPClient
}

func (q *QrCodeLogin) initClient() {
	q.httpClient = client.NewHTTPClient()
}

func (*QrCodeLogin) generateGid() (gid string, err error) {
	vm := otto.New()
	vm.Run(`
        function u(x, K) {
			x += '';
			for (var N = [], T = 0; T < K.length; T++) N[T % 4] ^= K.charCodeAt(T);
			var U = ['EC', 'OK'],
			V = [];
			V[0] = x >> 24 & 255 ^ U[0].charCodeAt(0);
			V[1] = x >> 16 & 255 ^ U[0].charCodeAt(1);
			V[2] = x >> 8 & 255 ^ U[1].charCodeAt(0);
			V[3] = x & 255 ^ U[1].charCodeAt(1);
			U = [];
			for (T = 0; T < 8; T++) U[T] = T % 2 == 0 ? N[T >> 1] : V[T >> 1];
			N = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
			V = '';
			for (T = 0; T < U.length; T++) {
				V += N[U[T] >> 4 & 15];
				V += N[U[T] & 15]
			}
			return V
        }

		function generate_gid() {
			return "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(e) {
				var t = 16 * Math.random() | 0
				var n = "x" === e ? t : 3 & t | 8;
				return n.toString(16)
			}).toUpperCase()
		}
        
		//var abc = '1'+'2'

		`)
	//使用vm.Call(函式名,nil,傳遞的引數,如果後面有多個引數,用逗號隔開就可以了)
	//value, _ := vm.Call("u", nil, "12345678", "234")
	//value, _ := vm.Get("abc")
	var value otto.Value
	value, err = vm.Call("generate_gid", nil)
	if err != nil {
		fmt.Println("generate gid error,error is:", err.Error())
		return
	}
	fmt.Println(value)
	gid = value.String()
	return
}

func (q *QrCodeLogin) getQrCode(gid string) (callBack string, respBody []byte, err error) {

	t := int64(time.Now().Unix() * 1000)
	t1 := t + 21232
	t2 := t1 + 4
	callBack = fmt.Sprintf("tangram_guid_%d", t)
	webUrl := fmt.Sprintf("https://passport.baidu.com/v2/api/getqrcode?lp=pc&qrloginfrom=pc&gid=%s&callback=%s&apiver=v3&tt=%d&tpl=netdisk&_=%d", gid, callBack, t1, t2)

	headers := map[string]string{
		"Accept":          "*/*",
		"Accept-Encoding": "gzip, deflate, br",
		"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
		"Connection":      "keep-alive",
		"Host":            "passport.baidu.com",
		"Referer":         "https://pan.baidu.com/",
		"Sec-Fetch-Dest":  "script",
		"Sec-Fetch-Mode":  "no-cors",
		"Sec-Fetch-Site":  "same-site",
		"User-Agent":      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
	}

	respBody, err = q.httpClient.Fetch(webUrl, http.MethodGet, headers, nil)
	if err != nil {
		fmt.Println("get image url error,error is:", err.Error())
		return
	}

	fmt.Println("res is:", string(respBody))
	return
}

func (q *QrCodeLogin) downloadQrCode(imageUrl string) (err error) {

	headers := map[string]string{
		"Accept":          "image/webp,image/apng,image/*,*/*;q=0.8",
		"Accept-Encoding": "gzip, deflate, br",
		"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
		"Connection":      "keep-alive",
		"Host":            "passport.baidu.com",
		"Referer":         "https://pan.baidu.com/",
		"Sec-Fetch-Dest":  "image",
		"Sec-Fetch-Mode":  "no-cors",
		"Sec-Fetch-Site":  "same-site",
		"User-Agent":      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
	}
	respBody, err := q.httpClient.Fetch(imageUrl, http.MethodGet, headers, nil)
	if err!=nil {
		panic(err)
	}

	file, err := ioutil.TempFile("", "*.png")
	if err != nil {
		panic(err)
	}
	//defer os.Remove(file.Name())

	_, err = file.Write(respBody)
	if err!=nil {
		panic(err)
	}
	file.Close()
	fmt.Println("qrcode image file path is:",file.Name())
	//imageFile, err := os.Create("F:\\test\\baidu.png")
	//defer imageFile.Close()
	//
	//if err != nil {
	//	return
	//}
	//
	//imageFile.Write(respBody)
	return
}

func (q *QrCodeLogin) queryQrCode(channelId string, gid string) (v string, err error) {

	for ; ; {

		time.Sleep(2 * time.Second)
		t := time.Now().Unix() * 1000
		callBack := fmt.Sprintf("tangram_guid_%d", t)
		t1 := t + 5
		t2 := t1 + 5

		webUrl := fmt.Sprintf("https://passport.baidu.com/channel/unicast?channel_id=%s&tpl=netdisk&gid=%s&callback=%s&apiver=v3&tt=%d&_=%d", channelId, gid, callBack, t1, t2)
		headers := map[string]string{
			"Accept":          "*/*",
			"Accept-Encoding": "gzip, deflate, br",
			"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
			"Connection":      "keep-alive",
			"Host":            "passport.baidu.com",
			"Referer":         "https://pan.baidu.com/",
			"Sec-Fetch-Dest":  "script",
			"Sec-Fetch-Mode":  "no-cors",
			"Sec-Fetch-Site":  "same-site",
			"User-Agent":      "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
		}

		var respBody []byte
		respBody, err = q.httpClient.Fetch(webUrl, http.MethodGet, headers, nil)
		if err != nil {
			fmt.Println("error is:", err.Error())
			return
		}

		reg := regexp.MustCompile(fmt.Sprintf("%s\\(([\\S]+?)\\)", callBack))
		res := reg.FindAllSubmatch(respBody, -1)

		if len(res) <= 0 {
			return
		}

		respBody = res[0][1]
		d := &QueryData{}
		fmt.Println("res is:", string(respBody))

		err = json.Unmarshal(respBody, d)
		if err != nil {
			fmt.Println("error is:", err.Error())
			return
		}

		channelV := d.ChannelV
		fmt.Println("channel v is:", channelV)
		var tempMap map[string]interface{}

		err = json.Unmarshal([]byte(channelV), &tempMap)
		if err != nil {
			fmt.Println("error is:", err.Error())
			continue
		}
		var ok bool
		v, ok = tempMap["v"].(string)
		if ok {
			fmt.Println("tempMap v is:", v)
			return
		}
	}

	return
}


func (q *QrCodeLogin) parseCallBackData(callBack string,respBody []byte) (parsedBody []byte,err error){

	if callBack == ""{
		return
	}
	reg := regexp.MustCompile(fmt.Sprintf("%s\\(([\\s\\S]+?)\\)", callBack))
	res := reg.FindAllSubmatch(respBody, -1)

	if len(res) <= 0 {
		err = errors.New("CallBackError")
		return
	}

	parsedBody = res[0][1]
	return
}

func (q *QrCodeLogin) login(fakeBduss string) (bduss string,err error) {

	t := time.Now().Unix() * 1000
	t1 := t + 225
	callBack:="bd__cbs__ay6xvs"
	webUrl := fmt.Sprintf("https://passport.baidu.com/v3/login/main/qrbdusslogin?v=%d&bduss=%s&loginVersion=v4&qrcode=1&tpl=netdisk&apiver=v3&tt=%d&traceid=&time=%d&alg=v3&callback=%s", t, fakeBduss, t, t1, callBack)
	webUrl = webUrl + "&u=https%253A%252F%252Fpan.baidu.com%252Fdisk%252Fhome"
	headers := map[string]string{
		"Accept":                    "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
		"Accept-Encoding":           "deflate, br",
		"Accept-Language":           "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
		"Cache-Control":             "max-age=0",
		"Connection":                "keep-alive",
		"Host":                      "passport.baidu.com",
		"Sec-Fetch-Dest":            "document",
		"Sec-Fetch-Mode":            "navigate",
		"Sec-Fetch-Site":            "none",
		"Sec-Fetch-User":            "?1",
		"Upgrade-Insecure-Requests": "1",
		"User-Agent":                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66",
	}
	var respBody []byte
	respBody, err = q.httpClient.Fetch(webUrl, http.MethodGet, headers, nil)
	if err != nil {
		fmt.Println("error is:", err.Error())
		return
	}

	respBody,err = q.parseCallBackData(callBack,respBody)
	if err!=nil{
		fmt.Println("parse callback data error",err.Error())
		return
	}
	fmt.Println("-----------------------------------------------------------------------------------------------")
	text := string(respBody)
	fmt.Println("res is:", text)
	// "Accept-Encoding":           "deflate, br", 去掉了gzip 解碼有點問題
	text = strings.ReplaceAll(text, "'", "\"") // 返回的格式中 'data' 可能是單引號 
	text = strings.ReplaceAll(text, " ", "")
	text = strings.ReplaceAll(text, "\\", "")
	fmt.Println("text is :", text)
	d := &LoginData{}
	err = json.Unmarshal([]byte(text), d)
	if err != nil {
		fmt.Println("error is:", err.Error())
		return
	}
	bduss = d.Data.Session.Bduss
	fmt.Println("-----------------------------------------------------------------------------------------------")
	fmt.Println("bduss is:", bduss)    // 只需要bduss的可以下車了
	fmt.Println("-----------------------------------------------------------------------------------------------")
	return
}

type LoginData struct {
	Data SessionData `json:"data"`
}

type SessionData struct {
	Session SessionInfo `json:"session"`
}
type SessionInfo struct {
	Bduss  string `json:"bduss"`
	Stoken string `json:"stoken"`
	Ptoken string `json:"ptoken"`
}

func (q *QrCodeLogin) getHomePage() {

	webUrl := "https://pan.baidu.com/disk/home"
	headers := map[string]string{
		"Host": "pan.baidu.com",
		"Connection": "keep-alive",
		"Upgrade-Insecure-Requests": "1",
		"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
		"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
		"Sec-Fetch-Site": "same-origin",
		"Sec-Fetch-Mode": "navigate",
		"Sec-Fetch-Dest": "document",
		"Referer": "https://pan.baidu.com/",
		"Accept-Language": "en,zh-CN;q=0.9,zh;q=0.8",
	}
	respBody,err:=q.httpClient.Fetch(webUrl,http.MethodGet,headers,nil)
	if err!=nil{
		fmt.Println("error is:",err.Error())
		return
	}
	fmt.Println("home page res is",string(respBody))
}

func (q *QrCodeLogin)getBdstoken()(bdstoken string,err error){

	webUrl := "https://tongxunlu.baidu.com"

	respBody,err:=q.httpClient.Fetch(webUrl,http.MethodGet,nil,nil)
	fmt.Println("bdstoken res is",string(respBody))
	if err != nil {
		fmt.Println("error is",err.Error())
		return
	}

	reg := regexp.MustCompile(`var bdstoken = '(.*?)'`)
	res := reg.FindAllSubmatch(respBody, -1)

	//fmt.Println("bdstoken is", string(res[0][1]))
	if len(res)>0{
		if len(res[0])>1{
			bdstoken=string(res[0][1])
		}
	}
	if bdstoken==""{
		err=errors.New("bdstoken not found")
	}
	return
}



func main() {

	q := &QrCodeLogin{}
	q.initClient()
	gid, err := q.generateGid()
	if err != nil {
		return
	}
	fmt.Println("gid is:", gid)

	callBack, respBody, err := q.getQrCode(gid)
	if err != nil {
		return
	}

	reg := regexp.MustCompile(fmt.Sprintf("%s\\(([\\S]+?)\\)", callBack))
	res := reg.FindAllSubmatch(respBody, -1)

	if len(res) <= 0 {
		return
	}

	respBody = res[0][1]
	d := &QrImageData{}

	err = json.Unmarshal(respBody, d)
	if err != nil {
		fmt.Println("error is:", err.Error())
		return
	}
	sign := d.Sign

	imageUrl := fmt.Sprintf("https://%s", d.ImageUrl)
	fmt.Println("image url is:", imageUrl)

	q.downloadQrCode(imageUrl)

	channelV, err := q.queryQrCode(sign, gid)
	if err != nil {
		fmt.Println("error is:", err.Error())
		return
	}
	fmt.Println("channelV is:", channelV)
	q.login(channelV)

	q.getHomePage()

	fmt.Println("------------------------------------------------------------")
	bdstoken,err:=q.getBdstoken()
	if err!=nil{
		fmt.Println("failed to get bdstoken,error is:",err.Error())
		return
	}

	fmt.Println("bdstoken is:",bdstoken)
}


找到輸出的路徑並用百度雲APP掃碼,確認登入就行,二維碼路徑在臨時目錄中:在這裡插入圖片描述