Go 语言网络翻译工具工程实践

一、项目介绍

本次 Go 语言工程实践课后作业旨在开发一个简易的命令行翻译工具,能够与特定的翻译 API(彩云小译)进行交互,实现英文单词到中文的翻译功能,并展示单词的发音(英音和美音)以及详细的中文释义。通过这个项目,深入理解 Go 语言在网络编程、数据处理以及 JSON 序列化与反序列化方面的应用。

二、实现思路

(一)构建请求结构体与数据准备

首先,定义了 DictRequest 结构体,用于构建向翻译 API 发送的请求数据格式。其中包含 TransType(翻译类型)、Source(待翻译的源单词)和 UserID 等字段。在 query 函数中,根据输入的单词创建 DictRequest 对象,并使用 json.Marshal 函数将其序列化为 JSON 格式的数据,以便后续作为请求体发送给 API。

(二)HTTP 请求构建与发送

利用 net/http 包构建 HTTP 请求。创建一个 http.Client 对象,它将负责发送请求并接收响应。然后,使用 http.NewRequest 函数创建一个 POST 请求,指定请求的目标 URL(彩云小译的翻译 API 地址)以及之前序列化好的 JSON 数据作为请求体。同时,为了使请求能够被 API 正确识别和处理,精心设置了一系列请求头信息,包括 ConnectionDNTUser-Agent 等,这些头信息模拟了浏览器的请求环境,确保 API 能够正常响应。

(三)响应处理与数据解析

发送请求后,通过 client.Do(req) 得到响应 resp。首先检查响应的状态码是否为 200,如果不是则表示请求出现错误,记录错误信息并终止程序。若状态码正常,则读取响应体内容 bodyText,并使用 json.Unmarshal 函数将其反序列化为 DictResponse 结构体对象。这个结构体定义了与 API 响应数据格式对应的字段,通过访问这些字段,能够获取到翻译结果中的英文发音(Dictionary.Prons.En 和 Dictionary.Prons.EnUs)以及中文释义(Dictionary.Explanations)等信息。

(四)命令行交互与程序入口

在 main 函数中,通过检查命令行参数的数量来确保用户正确输入了待翻译的单词。如果参数数量不正确,向标准错误输出打印使用提示信息并退出程序。若参数正确,则获取单词并调用 query 函数进行翻译查询与结果展示。

三、代码实现

type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}
  • 定义 DictRequest 结构体,用于构建发送给翻译 API 的请求数据结构。TransType 字段指定翻译类型(这里固定为 "en2zh"),Source 字段表示要翻译的源单词,UserID 字段暂时未使用但遵循 API 数据格式要求。结构体标签 json:"trans_type" 等用于指定 JSON 序列化和反序列化时的键名。
type DictResponse struct {
    Rc   int `json:"rc"`
    Wiki struct {
        KnownInLaguages int    `json:"known_in_laguages"`
        Description     struct {
            Source string      `json:"source"`
            Target interface{} `json:"target"`
        } `json:"description"`
        ID   string `json:"id"`
        Item struct {
            Source string `json:"source"`
            Target string `json:"target"`
        } `json:"item"`
        ImageURL  string `json:"image_url"`
        IsSubject string `json:"is_subject"`
        Sitelink  string `json:"sitelink"`
    } `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"`
}
  • 定义了 DictResponse 结构体,用于解析翻译 API 的响应数据。Rc 可能表示响应状态码或某种标识。Wiki 和 Dictionary 内部嵌套结构体,分别对应响应中不同部分的数据结构。例如,Dictionary 中的 Prons 结构体用于存储单词的发音(英音 En 和美音 EnUs),Explanations 切片用于存储中文释义等。同样,结构体标签用于 JSON 数据的处理。
func query(word string) {
    fmt.Println("querying...", word)
    // 创建 HTTP 客户端
    client := &http.Client{}
  • query 函数接受一个单词作为参数,首先打印出正在查询的提示信息。然后创建一个 http.Client 实例,它是进行 HTTP 请求的基础对象,通过它可以发送各种 HTTP 请求并处理响应。
// 构建请求对象并序列化
request := DictRequest{TransType: "en2zh", Source: word}
buf, err := json.Marshal(request)
if err!= nil {
    log.Fatal(err)
}
var data = bytes.NewReader(buf)
  • 创建 DictRequest 结构体实例,设置翻译类型为 "en2zh",源单词为传入的 word。接着使用 json.Marshal 将请求结构体序列化为 JSON 格式的字节切片,并存储在 buf 中。如果序列化过程出错,使用 log.Fatal 记录错误并终止程序。最后创建一个 bytes.NewReader,将序列化后的字节数据包装成一个 io.Reader 类型,以便作为请求体数据发送。
// 创建 HTTP POST 请求
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err!= nil {
    log.Fatal(err)
}
  • 使用 http.NewRequest 函数创建一个 HTTP POST 请求,指定请求方法为 "POST",目标 URL 为彩云小译的翻译 API 地址,请求体为之前创建的 data(包含序列化后的请求数据)。如果创建请求过程出错,同样使用 log.Fatal 记录错误并终止程序。
// 设置请求头
req.Header.Set("Connection", "keep-alive")
req.Header.Set("DNT", "1")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("device-id", "")
req.Header.Set("os-type", "web")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Sec-Fetch-Site", "cross-site")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
  • 为请求设置一系列请求头信息。这些请求头信息用于模拟浏览器环境,使 API 能够正确识别和处理请求。例如,Connection 设置连接保持,User-Agent 设置为常见的浏览器用户代理字符串,Content-Type 指定请求体为 JSON 格式且字符编码为 UTF-8,X-Authorization 设置授权令牌等。每个请求头都有其特定的作用,与 API 的要求和安全机制相关。
// 发送请求并获取响应
resp, err := client.Do(req)
if err!= nil {
    log.Fatal(err)
}
defer resp.Body.Close()
  • 使用创建的 http.Client 的 Do 方法发送请求,并获取响应 resp。如果发送请求过程出错,使用 log.Fatal 记录错误并终止程序。使用 defer 关键字确保在函数结束时关闭响应体,以释放相关资源。
// 读取响应体并处理
bodyText, err := ioutil.ReadAll(resp.Body)
if err!= nil {
    log.Fatal(err)
}
if resp.StatusCode!= 200 {
    log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
  • 使用 ioutil.ReadAll 读取响应体的全部内容到 bodyText 字节切片中。如果读取过程出错,使用 log.Fatal 记录错误并终止程序。接着检查响应的状态码,如果不是 200,表示请求出现问题,记录错误状态码和响应体内容并终止程序。
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err!= nil {
    log.Fatal(err)
}
  • 将读取到的响应体字节数据 bodyText 使用 json.Unmarshal 反序列化为 DictResponse 结构体实例 dictResponse。如果反序列化过程出错,使用 log.Fatal 记录错误并终止程序。
 // 打印翻译结果
    fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
    for _, item := range dictResponse.Dictionary.Explanations {
        fmt.Println(item)
    }
}
  • 从解析后的 DictResponse 结构体中获取单词的发音(英音 En 和美音 EnUs)并打印,然后遍历 Explanations 切片,打印出每个中文释义。

四、路径记录

(一)初期探索与环境搭建

在项目开始时,首先搭建了 Go 语言的开发环境,确保 net/httpencoding/json 等相关包能够正确安装和引用。然后,仔细研究了彩云小译 API 的文档,了解其请求格式、响应格式以及所需的请求头信息等关键内容。

(二)请求构建与调试

在构建 HTTP 请求过程中,容易遇到一些问题。例如,最初设置请求头时,由于对某些头信息的含义理解不够准确,导致请求发送后得不到正确的响应。可以通过查阅大量的网络资料以及对浏览器请求头的分析对比,逐步修正了请求头的设置,确保请求能够顺利到达 API 服务器并被正确处理。

(三)数据解析与错误处理

数据解析阶段,在将响应体反序列化为 DictResponse 结构体时,由于结构体定义与实际 API 响应数据不完全匹配,出现了反序列化错误。通过仔细检查 API 文档中的数据结构和代码中的结构体字段定义,可以发现一些字段类型或名称的差异,需要进行修正,使得数据能够正确解析。

(四)命令行交互优化

在完善命令行交互部分,最初只是简单地获取命令行参数并传递给 query 函数。但后来考虑到用户可能会输入错误的参数格式,添加了参数数量检查和友好的错误提示信息,提升了程序的易用性和健壮性。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐