这是我参与「第三届青训营 -后端场」笔记创作活动的的第二篇笔记。
项目实战
1.猜谜游戏
1.1 生成随机数
猜数游戏的本质就是,通过程序内部随机出一个数字,用户通过输入所猜的数字,判断并返回目标数与所猜数是否相等,如果相等则返回猜中信息,并退出程序,如果不相等则返回目标数与所猜数的大小关系。
首先为了实现随机出0-100之间的随机数,我们需要引入"math/rand"包,然后利用rand.Int()函数进行获取一个随机数,代码如下:
package main
import (
"fmt"
"math/rand"
)
func main() {
maxNum := 100
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
}
1.2 随机种子
采用上述代码生成数字,我们会发现每次产生的随机数都一样。这是因为程序内部存在一个随机种子,以此作为初始条件,然后用一定的算法不停迭代产生随机数,随机种子不变,则产生的随机数也是固定的。
因此我们需要调用rand.Seed(xxxx)函数,并以时间戳time.now.unix作为随机种子。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("The secret number is ", secretNumber)
}
现在可以正常输出0-100之间的随机数了。
1.3 读取用户输入
读取用户输入的数据,并解析成数字,我们可以添加以下代码就能获取我们输入的数据,并判断返回是否为数字类型。
var guess int
_, err := fmt.Scanf("%d", &guess) //获取输入并判断是否为int类型
if err != nil {
fmt.Println("不是数字类型,请输入数字") //如果不是int类型则输出错误
return
}
1.4 判断逻辑
将用户所猜的数字与被猜数进行比较判断,并且返回比较结果。
if guess > secretNumber {
fmt.Println("你猜的数字大于被猜数,请再输入一次")
} else if guess < secretNumber {
fmt.Println("你猜的数字小于被猜数,请再输入一次")
} else {
fmt.Println("恭喜你,猜中了这个数字")
}
1.5 实现循环
最后我们希望我们这个程序在用户猜到数字之后再退出,于是我们在我们输入的位置加上一个无限循环的for语句,整个猜数字流程的代码就实现了。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("请输入你要猜的数字")
for {
var guess int
_, err := fmt.Scanf("%d", &guess)
if err != nil {
fmt.Println("不是数字类型,请输入数字")
continue
}
fmt.Println("You guess is", guess)
if guess > secretNumber {
fmt.Println("你猜的数字大于被猜数,请再输入一次")
} else if guess < secretNumber {
fmt.Println("你猜的数字小于被猜数,请再输入一次")
} else {
fmt.Println("恭喜你,猜中了这个数字")
break //当猜中数字后跳出死循环
}
}
}
2.在线词典
2.1 抓包
熟练的掌握了 Go语言的各种包的使用,这次我们来使用第三方API来进行实现在线单词词典翻译功能,在这个项目当中,我们将利用 Go语言发送 HTTP 请求、解析 json 数据过来,并且学习如何使用代码生成来提高开发效率。
为了获取我们所需要的 API,我们需要通过抓包来获取
这次项目我们通过以彩云翻译为例,先打开彩云翻译的网页,然后右键网页选择检查或者按下 F12 来打开开发者工具:
然后我们找到网络一栏并且选择它,通过执行对good单词进行翻译,仔细查看网络,找到一个dict的文件,点击预览就会发现good单词通过API返回的json数据。
由于我们需要发送这个HTTP请求,但是用代码构建又会很麻烦,因此我们只需要右键选择dict,点击Copy,点击Copy as cURL(bash)。
curl "https://api.interpreter.caiyunai.com/v1/dict" ^
-H "Accept: application/json, text/plain, */*" ^
-H "Accept-Language: zh-CN,zh;q=0.9" ^
-H "Connection: keep-alive" ^
-H "Content-Type: application/json;charset=UTF-8" ^
-H "Origin: https://fanyi.caiyunapp.com" ^
-H "Referer: https://fanyi.caiyunapp.com/" ^
-H "Sec-Fetch-Dest: empty" ^
-H "Sec-Fetch-Mode: cors" ^
-H "Sec-Fetch-Site: cross-site" ^
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36" ^
-H "X-Authorization: token:qgemv4jr1y38jyq6vhvi" ^
-H "app-name: xy" ^
-H "device-id: " ^
-H "os-type: web" ^
-H "os-version: " ^
-H "sec-ch-ua: ^^" Not A;Brand^^";v=^^"99^^", ^^"Chromium^^";v=^^"101^^", ^^"Google Chrome^^";v=^^"101^^"" ^
-H "sec-ch-ua-mobile: ?0" ^
-H "sec-ch-ua-platform: ^^"Windows^^"" ^
--data-raw "^{^^"trans_type^^":^^"en2zh^^",^^"source^^":^^"good^^"^}" ^
--compressed
2.2 生成代码
通过Convert curl commands to code (curlconverter.com)这个网页,将代码粘贴到curl command,就可以获得以下代码(已加入解释注释)。有几个header比较复杂,生成代码有转义导致的编译错误,删掉这几行即可。
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
func main() {
client := &http.Client{}
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
// 创建请求
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
// 设置请求头
req.Header.Set("Connection", "keep-alive")
req.Header.Set("DNT", "1")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("device-id", "")
req.Header.Set("os-type", "web")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Sec-Fetch-Site", "cross-site")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
// 发起请求
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
// 关闭Body这个流,避免资源泄露
defer resp.Body.Close()
// 读取响应
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
}
我们将这一串代码copy到我们的编译器当中即可,然后我们运行这段代码,就会清楚的发现我们是能够发送请求,并且返回了一大串json数据。
2.3 request body
我们需要制作一个结构体来承载这个JSON数据的格式,因此我们找到dict文件对其右键,选择复制,复制其响应,然后通过网页JSON转Golang Struct - 在线工具 - OKTools选择嵌套类型并改名为DictResponse,就可以得到我们想要的结构体作为响应结构体。
为了使让我们的输入不再固定,因此我们需要创建一个结构体来代替我们的输入,对比请求代码,我们需要两个数据,一个是翻译类型,一个是被翻译对象。因此我们创建以下结构体,并使用json tag对其进行json处理:
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
}
通过初始化DictRequest结构体来发送请求数据,最后使用json.Unmarshal进行反序列化对返回的数据储存到DictResponse结构体中,我们就实现了如何发送要翻译的数据,并返回其翻译结果。
如果想要获取返回数据的特定输出,我们可以通过打印其结构体的某一部分即可,例如我们想要获取我们翻译过后单词的英式读法,和美式读法,我们可以通过以下代码打印出来即可:
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
2.4 完整代码
我们将其包装成一个函数,并且需要传入一个 string 类型的值,作为被翻译的字符串,最后通过main主函数来进行测试其功能,完整代码如下:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
type DictResponse struct {
Rc int `json:"rc"`
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"`
Description struct {
Source string `json:"source"`
Target interface{} `json:"target"`
} `json:"description"`
ID string `json:"id"`
Item struct {
Source string `json:"source"`
Target string `json:"target"`
} `json:"item"`
ImageURL string `json:"image_url"`
IsSubject string `json:"is_subject"`
Sitelink string `json:"sitelink"`
} `json:"wiki"`
Dictionary struct {
Prons struct {
EnUs string `json:"en-us"`
En string `json:"en"`
} `json:"prons"`
Explanations []string `json:"explanations"`
Synonym []string `json:"synonym"`
Antonym []string `json:"antonym"`
WqxExample [][]string `json:"wqx_example"`
Entry string `json:"entry"`
Type string `json:"type"`
Related []interface{} `json:"related"`
Source string `json:"source"`
} `json:"dictionary"`
}
func query(word string) {
client := &http.Client{}
request := DictRequest{TransType: "en2zh", Source: word}
// 序列化
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
// 创建请求
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
// 设置请求头
req.Header.Set("Connection", "keep-alive")
req.Header.Set("DNT", "1")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("device-id", "")
req.Header.Set("os-type", "web")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Sec-Fetch-Site", "cross-site")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
// 发起请求
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
// 关闭Body这个流,避免资源泄露
defer resp.Body.Close()
// 读取响应
bodyText, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}