Go语言的实战案例课后作业 | 豆包MarsCode AI 刷题

106 阅读6分钟

Go语言的实战案例课后作业

完成Go语言的实战案例课后作业,通过实操巩固Go语言的基础知识,本文主要展示实践过程中遇到的困难、解决思路以及部分代码(完整代码见文章末代码仓库)。如有任何反馈与问题,请与我联系!


准备工作

1. 实验环境

  • WSL Ubuntu 24.04 + VSCode

2. 代码准备

使用以下命令将项目文件克隆到本地环境

git clone git@github.com:wangkechun/go-by-example.git

作业内容

  1. 修改第一个例子猜谜游戏里面的最终代码,使用fmt.Scanf来简化代码实现
  2. 修改第二个例子命令行词典里面的最终代码,增加另一种翻译引擎的支持
  3. 在上一步骤的基础上,修改代码实现并行请求两个翻译引擎来提高响应速度

实践操作

作业一

查找文档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.NewSourcerand.New

src := rand.NewSource(time.Now().UnixNano())
r := rand.New(src)
secretNumber := r.Intn(maxNum)

作业二

我选择的是增加百度翻译引擎。结合课程中老师所讲以及3. requests实战——获取百度翻译的翻译信息_百度翻译采集-CSDN博客博客内容可知,应抓取的内容在network中名为sug的包中,注意是包含完整字母的sug包,比如我输入的是单词good,所以抓取了含有完整4个字母的sug包,预览结果如下图:

屏幕截图 2024-11-24 115730.png

使用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得到以下输出结果

屏幕截图 2024-11-24 115246.png

作业三

1. Go语言并发编程

并发操作可以同时发出多个翻译请求,加快整体响应时间,而不需要等待前一个请求完成后再启动。我使用了以下工具来完成并发编程

  • Goroutine:Go 的轻量级线程,用 go 关键字启动,允许函数异步执行。

  • sync.WaitGroup:用于管理并发任务,确保主线程等待所有并发任务完成。

2. 任务与目标

当前代码中,有两个主要任务:

  1. 请求 彩云翻译 API (query 函数)。
  2. 请求 百度翻译 API (baiduQuery 函数)。

目标:让这两个任务同时运行。

3. 使用 sync.WaitGroup 管理并发任务

sync.WaitGroup 提供三个主要方法:

  • Add(n):通知需要等待 n 个任务。
  • Done():通知一个任务完成。
  • Wait():阻塞主线程,直到所有任务完成。

应用在代码中:

  1. 在主线程中调用 wg.Add(2),表明有两个任务需要完成。
  2. 每个任务完成时调用 wg.Done()
  3. 主线程使用 wg.Wait() 等待,直到两个任务都完成。
4. 修改主要代码
  • main函数中设置sync.WaitGroup

    var wg sync.WaitGroup
    wg.Add(2)
    wg.Wait()
    
  • querybaiduQuery 中添加 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

image.png

可以发现几乎同时返回两个翻译结果,响应速度大大提高


总结

本次作业虽然码量不高,但却是一个实践Go语言的优秀案例,让我逐渐熟悉了Go语言的各种接口,并发操作以及json字符串的转化。


参考与资源

文章借鉴

  1. requests实战——获取百度翻译的翻译信息_百度翻译采集
    本文介绍了如何获取百度翻译的http请求。

代码仓库

  1. Yu-mi-kuang/Marscode-Training at feature/homework-01
    本次作业的完整实现代码。
  2. wangkechun/go-by-example
    克纯老师的源代码仓库

API 文档


感谢阅读!