在线字典入门go语言笔记(三) | 青训营

224 阅读6分钟

项目介绍

运用Golang来构建一个在线字典,用户可以在命令行里面查询一个字典。程序能够通过调用第三方的API查询到单词的信息(翻译信息、音标和例句等),并且能够将这些信息打印出来。(课后作业:修改第二个例子命令行词典里面的最终代码,增加另一种翻译引擎的支持)

实现方法

(1)抓包

以火山翻译这款在线翻译软件为例,先打开火山翻译的网页"translate.volcengine.com/" ,然后右键点击检查选项,打开浏览器的开发者工具。 在火山翻译的单词输入框内输入一个单词,然后点击翻译按钮,浏览器会发送一系列的请求,找到?msToken=&X-Bogus=DFSzswVLQDaZVWrltx-8XjNSwbuA&_signature选项卡,然后找到查询单词的请求。这是一个 HTTP 的 post 的请求,请求的 header 是一个json,里面有四个字段,word代表用户查询的单词,source代表翻译源使用了有道翻译服务,source_language表示源语言是英语,target_language表示目标语言是中文。API 的返回结果里面会有detail 和 extra 两个字段。需要用的结果主要在details.detail字段里面,其他字段里面还包翻译等信息。 3.png

(2)代码生成

需要在 Golang 里面去发送这个请求。但是这个请求比较复杂,用代码构造很麻烦。可以用一种简单的方式来生成代码,右键浏览器里面的 copy as curl,在终端粘贴一下 curl 命令,可以返回一大串 json。打开代码生成网站"curlconverter.com/go/" ,粘贴 curl 请求,即可自动生成 Golang 代码。

client := &http.Client{}
var data = strings.NewReader(`{"source":"youdao","words":["good"],"source_language":"en","target_language":"zh"}`)
req, err := http.NewRequest("POST", "https://translate.volcengine.com/web/dict/detail/v1/?msToken=&X-Bogus=DFSzswVuQDaZVWOztHeEbjNSwbmq&_signature=_02B4Z6wo00001JrA-dwAAIDDz0JyQ9yEWmyawP1AAEJ.ftoo8oXIUwfIsihFkekBkxt-VV1hImx-r-OUykXQLmvZ77rEZsUpbUzLbm.oeOpKIrKB8bJlIqo4u1shK16rMaeFSPP4BiHrC7RI82", data)
......
req.Header.Set("authority", "translate.volcengine.com")
req.Header.Set("accept", "application/json, text/plain, */*")
......
resp, err := client.Do(req)
......
defer resp.Body.Close()
bodyText, err := io.ReadAll(resp.Body)
  • http.Client{}表示程序创建了一个 HTTP 客户端对象,该客户端用于发送 HTTP 请求。
  • strings.NewReader({"source":"youdao","words"["good"],"source_language":"en","target_language":"zh"})表示使用 strings.NewReader 创建了一个包含请求体内容的 io.Reader 对象,以便发送 POST 请求时携带数据。
  • http.NewRequest("POST", "https://translate.volcengine.com/web/dict/detail/v1/?msToken=&X-Bogus=DFSzswVuQDaZVWOztHeEbjNSwbmq&_signature=_02B4Z6wo00001JrA-dwAAIDDz0JyQ9yEWmyawP1AAEJ.ftoo8oXIUwfIsihFkekBkxt-VV1hImx-r-OUykXQLmvZ77rEZsUpbUzLbm.oeOpKIrKB8bJlIqo4u1shK16rMaeFSPP4BiHrC7RI82", data)表示使用http.NewRequest 创建一个新的 HTTP 请求。指定了请求的方法(POST)和目标 URL。
  • 程序使用 req.Header.Set 方法设置了请求的各个头部信息,包括 AcceptContent-TypeUser-Agent 等。这些头部信息指定了请求的参数、格式和用户代理信息等。
  • client.Do(req)表示使用创建的客户端对象,通过 client.Do(req) 发送请求并接收响应。
  • defer resp.Body.Close()表示通过 defer 关键字,确保在函数结束时关闭响应体。
  • io.ReadAll(resp.Body)表示使用 io.ReadAll 从响应体中读取数据。

(3)生成request body

在 Golang 里面,需要生成一段 JSON ,常用的方式是先构造出来一个结构体,这个结构体和需要生成的 JSON 的结构是一一对应的。

type DictRequest struct {
    Source         string   `json:"source"`
    Words          []string `json:"words"`
    SourceLanguage string   `json:"source_language"`
    TargetLanguage string   `json:"target_language"`
}

这里用type DictRequest struct { ... }定义了一个名为 DictRequest 的结构体类型。其中,Source stringjson:"source" 表示翻译源,使用了json:"source"标签,这会在 JSON 编码和解码时将字段名映射为source

var data = strings.NewReader(`{"source":"youdao","words":["good"],"source_language":"en","target_language":"zh"}`)
替换成
request := DictRequest{Source: "youdao", Words: []string{word}, SourceLanguage: "en", TargetLanguage: "zh"}
buf, err := json.Marshal(request)
if err != nil {
    log.Fatal(err)
}
var data = bytes.NewReader(buf)

首先,初始化每个结构体成员,在调用json.Marshal来得到这个序列化之后的字符串,不同于之前那个字符串,这里是一个字符数组,所以需要把strings.NewReader改成bytes.NewReader来构造结构体上的主体部分,最后成功的进入一个变量来发送http请求。

(4)解析response body

接下来把这个 response body 来解析出来。在 js/Python 这些脚本语言里面,body 是一个字典或者 map 的结构, 可以直接从里面取值,但是 golang 是个强类型语言,这种做法并不是最佳实践。更常用的方式是和 request 的一样,写一个结构体,把返回的 JSON 反序列化到结构体里面。但是在浏览器里面可以看到这个 API 返回的结构非常复杂,如果要一一定义结构体字段,非常繁琐并且容易出错。 我们打开一个代码生成工具网站"oktools.net/json2go" ,把火山翻译上的json字符串粘贴上去,这样我们就能生成对应的结构体。 先定义一个dictResponsedictResponseHSData结构体对象,然后用json.UnmarshalbodyText反序列化到定义了的结构体内。

type DictResponse struct {
    Details []struct {
        Detail string `json:"detail"`
        Extra  string `json:"extra"`
    } `json:"details"`
    BaseResp struct {
        StatusCode    int    `json:"status_code"`
        StatusMessage string `json:"status_message"`
    } `json:"base_resp"`
}
type DictResponseHSData struct {
    Result []struct {
        Ec struct {
            Basic struct {
                Explains []struct {
                    Pos   string `json:"pos"`
                    Trans string `json:"trans"`
                } `json:"explains"`
            } `json:"basic"`
        } `json:"ec"`
    } `json:"result"`
}

(5)打印结果

观察那个 json 可以看出需要的结果是在 details.detail 里的,用 for range 循环来迭代它,然后直接打印结构,参照一些词典的显示方式,可以在前面打印出这个单词和它的翻译信息。 4.png

总结

在上面这个在线字典项目中,我学习到了一下go语言的知识点:

  1. 导入包(Import):代码开头使用import语句导入了多个标准库和第三方库,这些库提供了所需的功能,比如处理JSON、发起HTTP请求等。

  2. 结构体定义:代码中定义了三个结构体:DictResponseDictResponseHSDataDictRequest。这些结构体用于表示不同的JSON数据结构,以便在编码和解码JSON时进行处理。

  3. HTTP请求发送和处理:在query函数中,通过创建HTTP请求,设置请求头部,发送POST请求到指定的URL,然后从响应中读取并解析返回的JSON数据。代码中使用了http.Client来管理HTTP客户端,并使用http.NewRequest创建一个新的HTTP请求。

  4. JSON编码和解码:代码中使用了json.Marshal来将DictRequest结构体编码为JSON格式,然后使用json.Unmarshal解码返回的JSON数据到相应的结构体。

  5. 错误处理:在很多地方使用了错误处理,比如检查JSON编码和解码的错误,HTTP请求错误,以及处理HTTP状态码不为200的情况。

  6. 命令行参数:在main函数中,代码通过os.Args获取命令行参数,并使用第一个参数作为要翻译的单词。

  7. 循环和迭代:在解析JSON响应数据时,代码使用循环迭代展开嵌套的数据结构,以获取所需的翻译结果。

  8. 格式化输出:通过使用fmt.Printffmt.Fprintf,代码将信息打印到标准输出或标准错误输出。