激情六月丁香婷婷|亚洲色图AV二区|丝袜AV日韩AV|久草视频在线分类|伊人九九精品视频|国产精品一级电影|久草视频在线99|在线看的av网址|伊人99精品无码|午夜无码视频在线

高校合作1:010-59833514 ?咨詢電話:400-810-1418 服務(wù)與監(jiān)督電話:400-810-1418轉(zhuǎn)接2

golang 開發(fā)游戲(Golang實戰(zhàn)教程)

發(fā)布時間:2023-11-27 15:55:06 瀏覽量:189次

?Golang實戰(zhàn)教程

golang 開發(fā)游戲(Golang實戰(zhàn)教程)

本文為技術(shù)文章,閱讀對象為對Golang感興趣的朋友。

游戲:XX農(nóng)場(H5)。

功能:使用Golang模擬登陸,并自動完成“收茶”和“澆水”操作。

游戲界面

這是一個H5的農(nóng)場游戲,很常見的一個游戲。

游戲需要讓玩家先收茶然后再澆水。

做一次收茶澆水操作之后,需要等一定的時間后,再做重復(fù)做這個動作。

我們在這里不討論這個游戲項目,或者游戲體驗等問題。

我們只關(guān)于如何使用Golang自動完成這些操作。

要模擬登錄,就需要先分析這個游戲登錄需要哪些條件。

H5游戲,那當然使用Chrome這個神器咯。

登錄界面

我們從登錄界面可以看到,這個表單主要有三個控件:登錄賬戶、登錄密碼和記住賬號密碼。

我們使用Chrome來看看這個表單的具體情況。

從HTML代碼中,我們可以發(fā)現(xiàn),有兩個隱藏域,這個表單控件的具體情況如下:

  1. __VIEWSTATE
  2. __VIEWSTATEGENERATOR
  3. tb_loguid
  4. tb_pwd
  5. checkboxDefault
  6. Button1

登錄表單是使用Post方式提交,提交地址為:http://www.toutiao.com/a7056967701902295559/login.aspx。

換句話說,我們模擬登錄時,需要向?qū)Ψ椒?wù)器提交這些數(shù)據(jù)。

用戶名和密碼這兩個參數(shù),是我們自己填寫的。

__VIEWSTATE和__VIEWSTATEGENERATOR這兩個參數(shù)是服務(wù)器提供的,那么怎么處理呢?

方式倒也簡單。先直接使用Golang訪問這個登錄頁面,然后從頁面中獲取上面說的兩個參數(shù)的值。

獲取頁面參數(shù),我們使用goquery這個庫,很酷的一個庫,Golang仿jQuery的一個庫,很好用。

goquery源碼地址:
github.com/PuerkitoBio/goquery

本程序,除了goquery這個庫以外,其他的都使用標準庫。

然后我們?yōu)榱瞬蛔岋L控系統(tǒng)(如果有多話)那么容易發(fā)現(xiàn)我們是模擬登錄,那么我們就把瀏覽器相關(guān)的參數(shù)都給到服務(wù)器。

登錄之后,我使用標準庫中的cookiejar來維護Cookie。

游戲主頁地址做了隱藏。

廢話不多說,上代碼:

const HomePageURL = "http://www.xxxxxxx.cn/wap/" // 首頁
const LoginURL = HomePageURL + "login.aspx"      // 登錄頁面
const UserHome = HomePageURL + "userindex.aspx"  // 用戶中心
const TianYuanURL = HomePageURL + "tea.aspx"     // 田園頁面

const UserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"
const Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
const AcceptLanguage = "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6,mt;q=0.5,zh-TW;q=0.4,la;q=0.3,da;q=0.2,pl;q=0.1,lb;q=0.1,de;q=0.1"
const ContentType = "application/x-www-form-urlencoded"

// 請求客戶端
type HttpClient struct {
	// 公共頁面參數(shù)
	ViewState          string
	ViewStateGenerator string

	gCurCookies   []*http.Cookie
	gCurCookieJar *cookiejar.Jar

	myHttpClient *http.Client // 請求客戶端
}

// 登錄,獲取Cookie
func (h *HttpClient) Login(userName, password string) error {
	h.GetPageParams()
	params := url.Values{}
	params.Add("__VIEWSTATE", h.ViewState)
	params.Add("__VIEWSTATEGENERATOR", h.ViewStateGenerator)
	params.Add("tb_loguid", userName)
	params.Add("tb_pwd", password)
	params.Add("checkboxDefault", "0")
	params.Add("Button1", "確認登錄")
	postParams := ioutil.NopCloser(strings.NewReader(params.Encode()))

	req, err := http.NewRequest("POST", LoginURL, postParams)
	if err != nil {
		return err
	}

	req.Header.Add("Content-Type", ContentType)
	req.Header.Add("Accept", Accept)
	req.Header.Add("Accept-Encoding", "gzip, deflate")
	req.Header.Add("Accept-Language", AcceptLanguage)
	req.Header.Add("Cache-Control", "max-age=0")
	req.Header.Add("Connection", "keep-alive")
	req.Header.Add("Content-Length", "217")
	req.Header.Add("DNT", "1")
	req.Header.Add("Origin", "http://www.xxxxxxx.cn")
	req.Header.Add("Referer", LoginURL)
	req.Header.Add("Upgrade-Insecure-Requests", "1")
	req.Header.Add("User-Agent", UserAgent)

	resp, err := h.myHttpClient.Do(req)
	if err != nil {
		return err
	}

	h.gCurCookies = h.gCurCookieJar.Cookies(req.URL)
	defer resp.Body.Close()

	return nil
}

// 獲取頁面及Cookie參數(shù)
func (h *HttpClient) GetPageParams() error {
	_myCookieJar, _ := cookiejar.New(nil)
	h.gCurCookieJar = _myCookieJar
	h.myHttpClient = &http.Client{
		CheckRedirect: nil,
		Jar:           h.gCurCookieJar,
	}

	if err := h.getViewState(LoginURL); err != nil {
		return err
	}

	return nil
}

// 獲取頁面參數(shù)
func (h *HttpClient) getViewState(url string) error {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	res, err := h.myHttpClient.Do(req)
	if err != nil {
		return err
	}

	defer res.Body.Close()

	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		return err
	}

	doc.Find("#__VIEWSTATE").Each(func(i int, s *goquery.Selection) {
		h.ViewState = s.AttrOr("value", "")
	})

	doc.Find("#__VIEWSTATEGENERATOR").Each(func(i int, s *goquery.Selection) {
		h.ViewStateGenerator = s.AttrOr("value", "")
	})

	if url == LoginURL {
		h.gCurCookies = h.gCurCookieJar.Cookies(req.URL)
	}

	return nil
}

為了可以支持多賬戶,我們將用戶名和密碼作為參數(shù)傳遞給登錄方法(Login)。

GetPageParams這個方法,用來初始化cookiejar這個對象。

getViewState這個方法,用來獲取頁面的公共參數(shù)。

這幾行代碼,可以完成模擬登錄的操作。

golang發(fā)起http請求,使用的是標準庫:net/http。

如果對這個庫還不屬性的朋友,可以查閱一下官方的文檔。

大家可以使用單元測試進行嘗試。

如果做單元測試,這里就不展開了,搜索一下,網(wǎng)上相關(guān)的資料很多。

因為要支持多賬戶模擬,所以我們的程序中需要支持退出功能。

還是使用Chrome來分析這個游戲如何處理退出的。

從個人中心,拉到底部,我們發(fā)現(xiàn)了“退出登錄”的按鈕。查看源碼,發(fā)現(xiàn)退出使用的Get方法。地址為:userindex.aspx?act=out。

那就更簡單了,代碼如下:

// 退出登錄
func (h *HttpClient) Logout() error {
	req, err := http.NewRequest("GET", UserHome+"?act=out", nil)
	if err != nil {
		return err
	}

	res, err := h.myHttpClient.Do(req)
	if err != nil {
		return err
	}

	defer res.Body.Close()

	if res.StatusCode != 200 {
		return errors.New("退出登錄失敗,請檢查后重新操作")
	}

	return nil
}

登錄和退出完成了,我們現(xiàn)在開始干正事。

對于這個游戲,主要的游戲操作有收茶和澆水。

說明:當前使用的這個賬戶,還沒有種茶,當然界面上看不到茶樹。

在收茶之前,我們需要先檢查,是否有茶可收。

還是老規(guī)矩,分析頁面代碼。我們需要搞清楚收茶的動作是如何提交到服務(wù)器的。

我們在導(dǎo)航中點擊“田園”按鈕,進入游戲主界面。使用Chrome的開發(fā)者工具查看源碼。

我們從源碼中可以看到,這個游戲的開發(fā)者將整個頁面都放到了一個表單中。

golang 開發(fā)游戲(Golang實戰(zhàn)教程)

表單提交方法還是Post,表單提交地址為:http://www.toutiao.com/a7056967701902295559/tea.aspx。

這個頁面也存在著幾個隱藏域,而且有的還有值。

我們不要管這幾個隱藏域的參數(shù)做什么用,但是有一點我們很清楚:

在對游戲進行操作時,服務(wù)器需要這些參數(shù)。

你需要我給你就好了。

換句話說,我們可以這樣理解:

這個游戲的任何操作,都是使用Post方法,向服務(wù)器提交相應(yīng)的數(shù)據(jù)。

有了這個基礎(chǔ)了解,那么我們就知道要怎么操作了。

還是使用getViewState這個方法獲取頁面的隱藏域參數(shù)。

其他操作也是如此。


檢查是否可以收茶

我們前面提到過,要收茶,也需要有茶可收。

我們測試賬號現(xiàn)在沒有可收的茶葉。

如果有茶可以收的時候,服務(wù)器會在頁面中加入一個HTML節(jié)點。這個節(jié)點有個Class屬性,屬性值為“qipao”。

那么我們就在頁面中查找,如果出現(xiàn)了這個class值為qipao的節(jié)點,那么就可以操作收茶動作。

// 檢查是否可以收茶
func (h *HttpClient) checkCanTakeTea() (bool, error) {
	if err := h.getViewState(TianYuanURL); err != nil {
		return false, err
	}

	req, err := http.NewRequest("GET", TianYuanURL, nil)
	if err != nil {
		return false, err
	}

	res, err := h.myHttpClient.Do(req)
	if err != nil {
		return false, err
	}

	defer res.Body.Close()

	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		return false, err
	}

	// 如果可以收茶,則頁面會有 .qipao 這個節(jié)點
	if len(doc.Find(".qipao").Nodes) >= 1 {
		return true, nil
	}

	return false, nil
}

這個方法還是使用net/http這個庫。


模擬收茶操作

我們查看頁面中的JS代碼,JS代碼也不多,作用也比較簡單。

主要是頁面倒計時處理和游戲動作的操作。

我們分析JS代碼,可以知道,收茶動作其實就是向服務(wù)器提交這個頁面的參數(shù)。

有茶可收的情況下,會有一個__EVENTTARGET這個參數(shù)的值為LinkButton10。

那么收茶操作的代碼如下:

// 收茶操作
func (h *HttpClient) TakeTea() error {
	if err := h.getViewState(TianYuanURL); err != nil {
		return err
	}

	params := url.Values{}
	params.Add("__VIEWSTATE", h.ViewState)
	params.Add("__VIEWSTATEGENERATOR", h.ViewStateGenerator)
	params.Add("__EVENTTARGET", "LinkButton10")
	params.Add("__EVENTARGUMENT", "")
	postParams := ioutil.NopCloser(strings.NewReader(params.Encode()))
	req, err := http.NewRequest("POST", TianYuanURL, postParams)
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", ContentType)

	resp, err := h.myHttpClient.Do(req)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		return errors.New("收茶操作失??!請檢查并重新操作")
	}

	defer resp.Body.Close()

	return nil
}


模擬澆水操作

這個游戲的規(guī)則為,收茶之后,還需要向茶樹澆水。

澆水操作的原理和收茶類似,只是參數(shù)值不同。

這樣的話,那么事情就簡單了。

// 澆水操作
func (h *HttpClient) Watering() error {
	if err := h.getViewState(TianYuanURL); err != nil {
		return err
	}

	params := url.Values{}
	params.Add("__VIEWSTATE", h.ViewState)
	params.Add("__VIEWSTATEGENERATOR", h.ViewStateGenerator)
	params.Add("__EVENTTARGET", "")
	params.Add("__EVENTARGUMENT", "")
	params.Add("Button1", "確認澆水")
	postParams := ioutil.NopCloser(strings.NewReader(params.Encode()))
	req, err := http.NewRequest("POST", TianYuanURL, postParams)
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", ContentType)

	resp, err := h.myHttpClient.Do(req)
	if err != nil {
		return err
	}

	if resp.StatusCode != 200 {
		return errors.New("澆水操作失?。≌垯z查并重新操作")
	}

	defer resp.Body.Close()
	return nil
}


到這里,對這個游戲的主要的操作的模擬都有了。

接下來就是把這幾個動作串聯(lián)起來。

登錄->檢查是否可以收茶->收茶->澆水->退出登錄

這一系列的動作,我們把它看成一個任務(wù)。放到一個方法中。

// 執(zhí)行任務(wù)
func (h *HttpClient) TaskRun() error {
	for _, item := range users {
		// 1.登錄
		fmt.Println(tools.Timetostr(0), " 登錄...")
		if err := h.Login(item["userName"], item["password"]); err != nil {
			return err
		}

		// 2.檢查是否可以收茶
		fmt.Println(Timetostr(0), " 檢查...")
		status, err := h.checkCanTakeTea()
		if err != nil {
			return err
		}

		// 3.如果可以收茶,則收茶和澆水
		if status {
			if err := h.TakeTea(); err != nil {
				return err
			}

			fmt.Println(Timetostr(0), item["userName"]+" 收茶成功...")

			// 延時2分鐘
			time.Sleep(time.Duration(120) * time.Second)

			if err := h.Watering(); err != nil {
				return err
			}

			fmt.Println(Timetostr(0), item["userName"]+" 澆水成功...")
		}

		// 延時50秒
		time.Sleep(time.Duration(50) * time.Second)

		// 4.操作完畢,退出登錄
		fmt.Println(Timetostr(0), " 退出...")
		if err := h.Logout(); err != nil {
			return err
		}
	}

	return nil
}

// 獲取當前日期時間
func Timetostr(unixTime int64) string {
	if unixTime == 0 {
		unixTime = time.Now().Unix()
	}
	timer := time.Unix(unixTime, 0)
	return timer.Format(TimeLayoutLong)
}

為了能更好地模擬人工操作,我們在執(zhí)行完一個操作之后,加了一個延時。

同時將程序執(zhí)行的情況輸出到屏幕。

是用Golang做開發(fā),有個很方便的地方就是有強大的標準庫。

讓Golang定時執(zhí)行也是很方便的。

代碼如下:

// 需要監(jiān)控的賬戶
var users []map[string]string = []map[string]string{
	{
		"userName": "18*******44",
		"password": "********",
	},
	{
		"userName": "18*******20",
		"password": "********",
	},
}

func main() {
	fmt.Println("開始定時任務(wù)...")

	// 10分鐘執(zhí)行一次
	for range time.Tick(time.Duration(600) * time.Second) {
		httpClient := new(HttpClient)
		if err := httpClient.TaskRun(); err != nil {
			fmt.Println(err.Error())
		}
	}
}

這樣,多個賬戶,定時執(zhí)行就完成了。


更多精彩內(nèi)容發(fā)布于公眾號:代碼乾坤 (CoderLand)

golang 開發(fā)游戲(Golang實戰(zhàn)教程)

熱門課程推薦

熱門資訊

請綁定手機號

x

同學(xué)您好!

您已成功報名0元試學(xué)活動,老師會在第一時間與您取得聯(lián)系,請保持電話暢通!
確定