Go语言的实战案例课后作业
完成Go语言的实战案例课后作业,通过实操巩固Go语言的基础知识,本文主要展示实践过程中遇到的困难、解决思路以及部分代码(完整代码见文章末代码仓库)。如有任何反馈与问题,请与我联系!
准备工作
1. 实验环境:
- WSL Ubuntu 24.04 + VSCode
2. 代码准备:
使用以下命令将项目文件克隆到本地环境
git clone git@github.com:wangkechun/go-by-example.git
作业内容
- 修改第一个例子猜谜游戏里面的最终代码,使用fmt.Scanf来简化代码实现
- 修改第二个例子命令行词典里面的最终代码,增加另一种翻译引擎的支持
- 在上一步骤的基础上,修改代码实现并行请求两个翻译引擎来提高响应速度
实践操作
作业一
查找文档fmt package - fmt - Go Packages得到fmt.Scanf函数原型及说明:
func Scanf(format string, a ...any) (n int, err error)
Scanf scans text read from standard input, storing successive space-separated values into successive arguments as determined by the format. It returns the number of items successfully scanned. If that is less than the number of arguments, err will report why. Newlines in the input must match newlines in the format. The one exception: the verb %c always scans the next rune in the input, even if it is a space (or tab etc.) or newline.
知道使用方法后只需要将原代码中的读入改为以下即可
var guess int
_, err := fmt.Scanf("%d", &guess)
可以看出,比起原代码中调用的NewReader,fmt.Scanf确实大大简化了代码
另外,在go 1.20版本以后rand.Seed
已被弃用,应该改用rand.NewSource
和rand.New
。
src := rand.NewSource(time.Now().UnixNano())
r := rand.New(src)
secretNumber := r.Intn(maxNum)
作业二
我选择的是增加百度翻译引擎。结合课程中老师所讲以及3. requests实战——获取百度翻译的翻译信息_百度翻译采集-CSDN博客博客内容可知,应抓取的内容在network中名为sug的包中,注意是包含完整字母的sug包,比如我输入的是单词good,所以抓取了含有完整4个字母的sug包,预览结果如下图:
使用curl to Go工具将复制的curl命令转化为golang代码,结果如下:
package main
import (
"fmt"
"io"
"log"
"net/http"
"strings"
)
func main() {
client := &http.Client{}
var data = strings.NewReader(`kw=good`)
req, err := http.NewRequest("POST", "https://fanyi.baidu.com/sug", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Accept", "*/*")
req.Header.Set("Accept-Language", "en,zh-CN;q=0.9,zh;q=0.8,en-GB;q=0.7,en-US;q=0.6")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", `BAIDUID_BFESS=226BEA6CBEB4A1A76AF3E166E6B6B5D4:FG=1; BAIDU_WISE_UID=wapp_1717649397376_245; __bid_n=190200fa98d438446f0c3b; BIDUPSID=226BEA6CBEB4A1A76AF3E166E6B6B5D4; PSTM=1719148480; H_PS_PSSID=60277_60567_60574_60623_60628; BDUSS=QtSndESjBZMkg4WnBFV0lrM0lCUXBjRWl4Z3I5SnNBRDYyc2hWZzZwfi1VakJuSVFBQUFBJCQAAAAAAAAAAAEAAACSvxdmyuXK5crlztKwoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7FCGf-xQhnaV; BDUSS_BFESS=QtSndESjBZMkg4WnBFV0lrM0lCUXBjRWl4Z3I5SnNBRDYyc2hWZzZwfi1VakJuSVFBQUFBJCQAAAAAAAAAAAEAAACSvxdmyuXK5crlztKwoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7FCGf-xQhnaV; MCITY=-75%3A; ZFY=:BC9Qw3Cc1xtsvTwA:BNAibXRmDdUNeA1JXzRsnMKqfFA:C; ZD_ENTRY=bing; BA_HECTOR=8g0h2h2g81a0aka12h24ah8kb6s2tf1jk3rl71u; arialoadData=false; ab_sr=1.0.1_NTYzN2NiYmQwNDZhNTUxYjlmZWQ4NTg5NDMwNTExNWM0MWRjYWY5NzM5MDhmM2I3NGQ3MzkzOGE4NTU2MGI1MzA3ODg2MTdmYTI2ZWQxYzFhYWVhNGUxZDFkMzc2OWZlNzIyNjI2NGM3NmY1ODI0MzBjOTVhMTk5NTE3Y2E5NDY2NGNkMjQxZjU1ODUyYzc3ZGJhYzJiNzlhODk3MTViNw==; RT="z=1&dm=baidu.com&si=8f76bc14-0230-4c17-aec8-782c7502e917&ss=m3uc673n&sl=1&tt=2f3&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=372"`)
req.Header.Set("Origin", "https://fanyi.baidu.com")
req.Header.Set("Referer", "https://fanyi.baidu.com/mtpe-individual/multimodal")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-origin")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0")
req.Header.Set("sec-ch-ua", `"Microsoft Edge";v="131", "Chromium";v="131", "Not_A Brand";v="24"`)
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
}
观察json结构体data发现仅包含kw即keyword,由此可以构造request body:
type BaiduRequest struct {
Keyword string `json:"kw"`
}
接下来再利用JSON转Golang Struct - 在线工具 - OKTools将json文件转化成response body:
type BaiduResponse struct {
Errno int `json:"errno"`
Data []struct {
K string `json:"k"`
V string `json:"v"`
} `json:"data"`
Logid int64 `json:"logid"`
}
其他仿照示例项目代码修改并补充代码,最后输出百度翻译响应的Data键与键值
for _, item := range baiduResponse.Data {
fmt.Printf("%s %s\n", item.K, item.V)
}
整理后执行命令go run main.go hello
得到以下输出结果
作业三
1. Go语言并发编程
并发操作可以同时发出多个翻译请求,加快整体响应时间,而不需要等待前一个请求完成后再启动。我使用了以下工具来完成并发编程
-
Goroutine:Go 的轻量级线程,用
go
关键字启动,允许函数异步执行。 -
sync.WaitGroup:用于管理并发任务,确保主线程等待所有并发任务完成。
2. 任务与目标
当前代码中,有两个主要任务:
- 请求 彩云翻译 API (
query
函数)。 - 请求 百度翻译 API (
baiduQuery
函数)。
目标:让这两个任务同时运行。
3. 使用 sync.WaitGroup
管理并发任务
sync.WaitGroup
提供三个主要方法:
Add(n)
:通知需要等待n
个任务。Done()
:通知一个任务完成。Wait()
:阻塞主线程,直到所有任务完成。
应用在代码中:
- 在主线程中调用
wg.Add(2)
,表明有两个任务需要完成。 - 每个任务完成时调用
wg.Done()
。 - 主线程使用
wg.Wait()
等待,直到两个任务都完成。
4. 修改主要代码
-
在
main
函数中设置sync.WaitGroup
:var wg sync.WaitGroup wg.Add(2) wg.Wait()
-
在
query
和baiduQuery
中添加wg.Done()
:调用
wg.Done()
表示当前任务结束,以baiduQuery
函数为例:func baiduQuery(word string, wg *sync.WaitGroup) { defer wg.Done() // 确保任务完成后通知 WaitGroup // 请求百度翻译 }
-
用
goroutine
开始翻译请求 使用go
关键字启动并发执行:go query(word, &wg) go baiduQuery(word, &wg)
5. 运行与验证
执行指令go run ./simpledict/v6/main.go hello
可以发现几乎同时返回两个翻译结果,响应速度大大提高
总结
本次作业虽然码量不高,但却是一个实践Go语言的优秀案例,让我逐渐熟悉了Go语言的各种接口,并发操作以及json字符串的转化。
参考与资源
文章借鉴
- requests实战——获取百度翻译的翻译信息_百度翻译采集
本文介绍了如何获取百度翻译的http请求。
代码仓库
- Yu-mi-kuang/Marscode-Training at feature/homework-01
本次作业的完整实现代码。 - wangkechun/go-by-example
克纯老师的源代码仓库
API 文档
- Go语言API文档
详细说明了关于fmt.Scanf函数的使用方法 - 百度翻译API官方文档
包含接口的详细参数说明及注意事项。 - curl to Go
用于将curl命令转化为Golang的工具,提升效率。 - OKTools
用于将json字符串转化为Golang的工具。
感谢阅读!