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

132 阅读5分钟

项目介绍

运用Golang来构建一个在线字典,用户可以在命令行里面查询一个字典。程序能够通过调用第三方的API查询到单词的信息(翻译信息、音标和例句等),并且能够将这些信息打印出来。

实现方法

(1)抓包

以彩云小译这款在线翻译软件为例,先打开彩云小译的网页"fanyi.caiyunapp.com/" ,然后右键点击检查选项,打开浏览器的开发者工具。
在彩云小译的单词输入框内输入一个单词,然后点击翻译按钮,浏览器会发送一系列的请求,找到dict选项卡,然后找到查询单词的请求。这是一个 HTTP 的 post 的请求,请求的 header 是一个json,里面有两个字段,一个代表用户是要从什么语言转到什么语言,source代表用户查询的单词。API 的返回结果里面会有 Wiki 和 dictionary 两个字段。需要用的结果主要在dictionary.Explanations 字段里面,其他字段里面还包括音标等信息。
1.png

(2)代码生成

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

client := &http.Client{} 
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`) 
req, err := http.NewRequest("POST", "https://lingocloud.caiyunapp.com/v1/dict", data)
......
req.Header.Set("authority", "api.interpreter.caiyunai.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({"trans_type":"en2zh","source":"good"})表示使用 strings.NewReader 创建了一个包含请求体内容的 io.Reader 对象,以便发送 POST 请求时携带数据。
  • http.NewRequest("POST", "https://lingocloud.caiyunapp.com/v1/dict", 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 {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}

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

var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`) 
替换成
request := DictRequest{TransType: "en2zh", Source: word}
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字符串粘贴上去,这样我们就能生成对应的结构体。
先定义一个dictResponse结构体对象,然后用json.UnmarshalbodyText反序列化到这个定义了的结构体内。

type DictResponse struct {
    Rc   int `json:"rc"`
    Wiki struct {
    } `json:"wiki"`
    Dictionary struct {
        Prons struct {
            EnUs string `json:"en-us"`
            En   string `json:"en"`
        } `json:"prons"`
        Explanations []string      `json:"explanations"`
        Synonym      []string      `json:"synonym"`
        Antonym      []string      `json:"antonym"`
        WqxExample   [][]string    `json:"wqx_example"`
        Entry        string        `json:"entry"`
        Type         string        `json:"type"`
        Related      []interface{} `json:"related"`
        Source       string        `json:"source"`
    } `json:"dictionary"`
}
......
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)

(5)打印结果

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

2.png

总结

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

  1. HTTP 请求
  • 使用 http.Client{} 创建 HTTP 客户端对象。
  • 使用 http.NewRequest 创建一个新的 HTTP 请求。
  • 设置请求头部信息,如 req.Header.Set
  1. JSON 编码与解码
  • 定义了两个结构体 DictRequest 和 DictResponse,并使用了 json 标签指定字段的 JSON 名称。
  • 使用 json.Marshal 将结构体编码为 JSON 格式的字节切片。
  • 使用 json.Unmarshal 将 JSON 数据解码为结构体。
  1. 命令行参数处理
  • 使用 os.Args 获取命令行参数。
  • 检查命令行参数的数量,根据需求进行处理。
  1. 文件操作
  • 使用 os.Exit 终止程序。
  • 使用 fmt.Fprintf 打印帮助信息。
  1. 循环
  • 在 main 函数中使用 for 循环遍历 dictResponse.Dictionary.Explanations 中的每个元素。
  1. 错误处理
  • 在许多地方使用 err 进行错误检查,并使用 log.Fatal 打印错误信息并退出程序。