Go后端课三个案例实战课后作业 | 青训营笔记

166 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天。

这篇笔记旨在复习和分析go后端第一节课留下的三个作业:

1、修改猜谜游戏的代码,使用fmt.scnaf简化代码

2、修改在线词典的代码,增加另一种翻译引擎

3、在上一步的基础上,修改代码并行请求两个翻译引擎来提高响应速度

1、猜谜游戏

猜谜游戏中的输入流代码十分复杂,可以直接用fmt.scanf来简化。首先来看看scanf的定义。

func Scanf(format string, a ...interface{}) (n int, err error)

scanf函数根据format参数指定的格式将输入内容传值给参数a,返回值n是扫描成功的数量,error是遇到的错误。

对于猜谜游戏,我们要输入的是int型变量,因此猜谜游戏的输入代码如下:

    var guess int
    _, err := fmt.Scanf("%d\n", &guess)
    if err != nil {
       fmt.Println("An error occured while reading input. Please try again.Error is", err)
    }

上述代码中,我不需要扫描成功的数量,因此用_代替变量。我要输入的是整数,因此scanf的format参数是%d。

运行结果如下: image.png

2、在线词典——有道词典

我选择的第二个翻译引擎是有道词典。

打开有道词典www.youdao.com/ ,随便输入一个单词,按f12打开开发者模式,在network里找到一个jsonapi开头的请求。它的payload和preview如图所示:

image.png

image.png 可以看到有道词典的参数有q,le,t,client,sign和keyfrom,要处理我们发起的翻译请求,只需要q和le这两个参数即可。 这里要注意的是有道词典用的是Form Data的方式传参发起请求,参数格式是"q=dance&le=en",如果是彩云小译的Request Payload方式,参数格式是Json格式。

我们将请求的curl复制,进入Convert curl commands to Go (curlconverter.com)将curl转换为Go语言。之后,为了将有道词典的Json格式的Response转换为Go语言结构体,进入JSON转Golang Struct - 在线工具 - OKTools转换。

有道词典的Resonse非常长,全部复制下来绝大部分代码都没有用,因为我们只需要单词的音标和翻译即可,因此需要在Preview里找到关键信息,经过寻找,我们需要的关键信息均在ec中,如图所示:

image.png 经过上述准备,就可以用go制作基于有道词典的在线词典了。代码如下:

结构体

   type DictResponse2 struct {
       Ec struct {
          WebTrans []string `json:"web_trans"`
          Special  []struct {
             Nat   string `json:"nat"`
             Major string `json:"major"`
          } `json:"special"`
          ExamType []string `json:"exam_type"`
          Source   struct {
             Name string `json:"name"`
             URL  string `json:"url"`
          } `json:"source"`
          Word struct {
             Usphone  string `json:"usphone"`
             Ukphone  string `json:"ukphone"`
             Ukspeech string `json:"ukspeech"`
             Trs      []struct {
                Pos  string `json:"pos,omitempty"`
                Tran string `json:"tran"`
             } `json:"trs"`
             Wfs []struct {
                Wf struct {
                   Name  string `json:"name"`
                   Value string `json:"value"`
                } `json:"wf"`
             } `json:"wfs"`
             ReturnPhrase string `json:"return-phrase"`
             Usspeech     string `json:"usspeech"`
          } `json:"word"`
     } `json:"ec"`
}

请求函数

func query2(word string) {
   client := &http.Client{}
   var data = strings.NewReader("q=" + word + "&le=en&client=web")  //Form Data格式的参形式,直接用strings.NewReader()创建Reader对象即可
   //以下代码由curl转go复制而来
   req, err := http.NewRequest("POST", "https://dict.youdao.com/jsonapi_s?doctype=json&jsonversion=4", data)
   if err != nil {
      log.Fatal(err)
   }
   req.Header.Set("Accept", "application/json, text/plain, */*")
   req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
   req.Header.Set("Connection", "keep-alive")
   req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   req.Header.Set("Cookie", "OUTFOX_SEARCH_USER_ID=1474111096@10.112.57.88; OUTFOX_SEARCH_USER_ID_NCOO=1361789659.5254738")
   req.Header.Set("Origin", "https://www.youdao.com")
   req.Header.Set("Referer", "https://www.youdao.com/")
   req.Header.Set("Sec-Fetch-Dest", "empty")
   req.Header.Set("Sec-Fetch-Mode", "cors")
   req.Header.Set("Sec-Fetch-Site", "same-site")
   req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
   req.Header.Set("sec-ch-ua", `"Not_A Brand";v="99", "Google Chrome";v="109", "Chromium";v="109"`)
   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 := ioutil.ReadAll(resp.Body)
   if err != nil {
      log.Fatal(err)
   }
   //以上代码由curl转go复制而来
   if resp.StatusCode != 200 {
      log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
   }
   var dictResponse2 DictResponse2
   err = json.Unmarshal(bodyText, &dictResponse2)
   if err != nil {
      log.Fatal(err)
   }
   //找到音标的位置是Ukphone和UsPhone,翻译结果在Trs中
   fmt.Println(word, "UK:", dictResponse2.Ec.Word.Ukphone, "US:", dictResponse2.Ec.Word.Usphone)
   for _, item := range dictResponse2.Ec.Word.Trs {
      fmt.Println(item)
   }
}

主函数

   func main() {
   if len(os.Args) != 2 {
      fmt.Fprintf(os.Stderr, `usage: simpleDict WORDexample: simpleDict hello`)
      os.Exit(1)
   }
   word := os.Args[1]
   query2(word)

运行结果如下:

image.png

3、并行请求两个翻译引擎

Go语言中的并行可以用代码go来实现。go func就相当于创建了一个GoRoutine协程,协程是轻量级的线程,是在用户态中的,即使创建大量的协程也不会影响到内核态,可以通过这些协程进行并行操作,这也是Go语言的一大特点——并行。并行执行代码可以有效提升代码运行效率。并行请求两个翻译引擎的代码如下:

    var wg sync.WaitGroup
    wg.Add(1) //只需要执行一个即可
    go func(word string) {
       query2(word)
       defer wg.Done() //执行完成,计数器 - 1
    }(word)
    go func(word string) {
       query(word)
       defer wg.Done() //执行完成,计数器 - 1
    }(word)
    wg.Wait() //阻塞直到计数器为0

运行结果只会出现一个翻译结果,在我的实验结果中只出现了彩云小译的翻译,推测是请求彩云小译的速度比有道词典更快,运行结果如下:

image.png

4、总结

以上就是我对这次作业的总结和分析。本人水平有限,可能有的地方写的不对,欢迎大家指正!