后端入门 - Go 语言原理与实践课后作业2、3分析(下)| 青训营笔记

187 阅读3分钟

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

本次青训营笔记将对Go语言原理与实践课后作业2、3进行分析。

课后作业内容

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

添加搜索引擎

打开浏览器的开发者模式,选择网络,我们找到名称为jsonapi_s?doctype=json&jsonversion=4的那一项,复制它的cURL,粘贴到curlconverter.com/go/ 里就可以自动生成go语言的代码了。

func main() {
   client := &http.Client{}
   var data = strings.NewReader(`q=dog&le=en&t=0&client=web&sign=f636931e046ba0b6c830dd77b8c3813d&keyfrom=webdict`)
   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,en;q=0.8")
   req.Header.Set("Connection", "keep-alive")
   req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
   req.Header.Set("Cookie", "OUTFOX_SEARCH_USER_ID_NCOO=575833337.077833; __yadk_uid=Y9HFscIgGEaDS1f830cPnD5YGD2KGWhj; OUTFOX_SEARCH_USER_ID=1015312947@101.229.163.249; advertiseCookie=advertiseValue; ___rl__test__cookies=1674220006117")
   req.Header.Set("Origin", "https://dict.youdao.com")
   req.Header.Set("Referer", "https://dict.youdao.com/result?word=dog&lang=en")
   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 (Macintosh; Intel Mac OS X 10_15_7) 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", `"macOS"`)
   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)
   }
   fmt.Printf("%s\n", bodyText)
}

NewReader中的q记录了需要翻译的单词,我们通过修改字符串就能让翻译引擎翻译其他单词了。这段代码中的bodyText包含了单词的音标和中文词义,因此我们需要对bodyText进行解析,获得我们需要的部分。
首先,我们需要构建一个结构体,用来存放解析后的数据。

type DictResponse struct {
   Ec struct {
      Word struct {
         Usphone string `json:"usphone"`
         Ukphone string `json:"ukphone"`
         Trs     []struct {
            Pos  string `json:"pos,omitempty"`
            Tran string `json:"tran"`
         } `json:"trs"`
      } `json:"word"`
   } `json:"ec"`
}

然后调用json.Unmarshal将bodyText里的数据解析并保存到结构体dictResponse中。

var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
   log.Fatal(err)
}

结构体中的Ukphone和Usphone分别是单词的英音音标和美音音标,Pos是单词词性,Tran是中文词义,这些信息均需要进行打印。

fmt.Printf("%v UK:[%v] US:[%v]\n", word, dictResponse.Ec.Word.Ukphone, dictResponse.Ec.Word.Usphone)
for _, item := range dictResponse.Ec.Word.Trs {
   fmt.Println(item.Pos, item.Tran)
}

并行翻译

以上我们完成了添加搜索引擎的任务,接下来我们将在上一步骤的基础上,修改代码实现并行请求两个翻译引擎来提高响应速度。
首先,我们将彩云小译和有道翻译翻译这两个搜索引擎都包装成函数,分别为query1()和query2(),然后调用go func把这两个函数变为协程,使他们并行执行。此外,为了防止主函数先于子函数执行完毕导致子函数被强制终止,我们需要使用sync.WaitGroup来进行程序计数,当query1()和query2()都执行完成时,程序计数为0,这时允许主函数退出。

func main() {
   word := "dog"
   var wg sync.WaitGroup
   wg.Add(2)
   go func() {
      query1(word)
      defer wg.Done()
   }()
   go func() {
      query2(word)
      defer wg.Done()
   }()
   wg.Wait()
}

以下是程序执行效果。

c1a2584ddd77c501c4f7310cae2669a0.png
经过多次运行可以发现,彩云小译的执行速度均快于搜狗词典,这可能跟服务器的响应速度有关。