这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
本次青训营笔记将对Go语言原理与实践课后作业2、3进行分析。
课后作业内容
- 简化go输入:修改第一个例子猜谜游戏里面的最终代码,使用fmt. Scanf来简化代码实现。
- 添加搜索引擎:修改第二个例子命令行词典里面的最终代码,增加另一种翻译引擎的支持。
- 并行翻译:在上一步骤的基础上,修改代码实现并行请求两个翻译引擎来提高响应速度。
添加搜索引擎
打开浏览器的开发者模式,选择网络,我们找到名称为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()
}
以下是程序执行效果。
经过多次运行可以发现,彩云小译的执行速度均快于搜狗词典,这可能跟服务器的响应速度有关。