Go学习笔记——在线词典项目|青训营

267 阅读6分钟

ヾ(≧▽≦*)o参与「第六届青训营——后端 」的第 3 天

「项目简介」

  • 该项目用Go实现一个在线词典青春版,能通过调用第三方的API查询到单词的翻译并打印
  • 在项目实施的过程中,我学习了网页抓包代码生成HTTP请求JSON解析获取命令行参数等操作
  • 本项目实现了并行请求百度翻译、彩云翻译这两个翻译引擎提高响应速度

「实现步骤」

1.网页抓包

  • 打开翻译网页(这边使用到的彩云翻译),网页右键选择检查或按键盘F12打开网页控制台 微信图片_20230727232302.png
  • 输入需要查询的单词,点击翻译,浏览器会发送一段请求,在控制台上方选择网络(Network),选中dict,注意请求方法为POST

微信图片_20230727232812.png

  • 这是一个HTTP的POST请求,请求头(Header)是一个JSON,里面有两个字段,代表要翻译的语言类型和所查询的单词。

微信图片_20230727233609.png

  • API的返回结果会有Wiki、dictionary两个字段。其中dictionary包含单词意思、音标、同义词以及例句等内容。

微信图片_20230727234114.png

2.代码生成

  • 需要在 Golang 里面去发送这个请求。但是这个请求比较复杂,用代码构造很麻烦。可以用一种简单的方式来生成代码,右键dict,点击复制(copy)->复制cURL(bash)(copy as cURL)

curl(cmd) 是使用命令行进行网络请求的一种方式,而 curl(bash) 是使用 Bash 脚本来执行 curl 命令的方式。

微信图片_20230727234355.png

得到内容如下

curl 'https://api.interpreter.caiyunai.com/v1/dict' \
  -H 'authority: api.interpreter.caiyunai.com' \
  -H 'accept: application/json, text/plain, */*' \
  -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
  -H 'app-name: xy' \
  -H 'content-type: application/json;charset=UTF-8' \
  -H 'device-id: 116c48851cbfe197c1dd9a3d53d928d7' \
  -H 'origin: https://fanyi.caiyunapp.com' \
  -H 'os-type: web' \
  -H 'os-version;' \
  -H 'referer: https://fanyi.caiyunapp.com/' \
  -H 'sec-ch-ua: "Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-platform: "Windows"' \
  -H 'sec-fetch-dest: empty' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-site: cross-site' \
  -H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183' \
  -H 'x-authorization: token:qgemv4jr1y38jyq6vhvi' \
  --data-raw '{"trans_type":"en2zh","source":"unbelievable"}' \
  --compressed

微信图片_20230727235612.png Go语言代码如下

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
)

func main() {
	client := &http.Client{}
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"unbelievable"}`)
        //创建请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
        //设置请求头
	req.Header.Set("authority", "api.interpreter.caiyunai.com")
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")
	req.Header.Set("app-name", "xy")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "116c48851cbfe197c1dd9a3d53d928d7")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
        
        //发起请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
        //读取响应
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
        //打印JSON字符串
	fmt.Printf("%s\n", bodyText)
}

&http.Client{} 是 Go 语言中创建一个 http.Client 类型的对象。http.Client 是用于发送 HTTP 请求并处理响应的客户端。通过创建一个 http.Client 对象,我们可以使用其提供的方法来发送 GET、POST 等类型的 HTTP 请求,并处理响应数据。该对象可以用于设置请求超时时间、设置自定义的 Transport、Cookie 策略等。

client.Dohttp.Client 对象的方法之一,用于发送 HTTP 请求并返回响应结果。通过调用 client.Do(request),其中 request 是一个 http.Request 对象,我们可以执行具体的 HTTP 请求操作。这个方法会阻塞当前的 goroutine,直到请求完成并返回一个 http.Response 对象,或者发生了错误。我们可以使用返回的 http.Response 对象来获取响应的状态码、响应头以及响应体等相关信息。

3.生成Request body

  • 在 Golang 里面,需要生成一段 JSON ,常用的方式是先构造出来一个结构体,这个结构体和需要生成的 JSON 的结构是一一对应的
// 彩云翻译
// 定义一个结构体,字段名字与json字段一致即可
type DictRequestCaiYun struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

4.解析Request body

  • 下面要对这个响应体进行解析。在Python等脚本语言中,响应体通常是一个字典或映射(map)结构,可以直接从中提取值。然而,Golang是一种强类型语言,这种做法并非最佳实践。更常见的做法是像处理请求一样,创建一个结构体,将返回的JSON反序列化到该结构体中。然而,在浏览器中可以看到此API返回的结构非常复杂,如果要逐一定义结构体字段,将非常繁琐且容易出错。

微信图片_20230728001446.png

  • 因为response返回的数据非常多而且复杂,自己书写起来很容易出错,所以我们可以通过JSON转结构体工具来生成代码。打开这个网站,把浏览器response的内容复制到网站里点击转换-嵌套就能生成对应的结构体。

微信图片_20230728001836.png

微信图片_20230728002017.png 代码如下

type AutoGenerated 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 []interface{} `json:"antonym"`
		WqxExample []interface{} `json:"wqx_example"`
		Entry string `json:"entry"`
		Type string `json:"type"`
		Related []interface{} `json:"related"`
		Source string `json:"source"`
	} `json:"dictionary"`
}
  • 接下来是对请求响应体进行JSON反序列化
//打印最终的翻译结果
	fmt.Println("---------彩云翻译---------")
	fmt.Printf("\n")
	if ('a' <= word[0] && word[0] <= 'z') || ('A' <= word[0] && word[0] <= 'Z') {
		fmt.Println(word, "UK:"+dictResponse.Dictionary.Prons.En, "US:"+dictResponse.Dictionary.Prons.EnUs)
	}
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
	//先确定最长的 item[0] 的长度,然后使用该长度来对齐输出。
	maxLen := 0
	for _, item := range dictResponse.Dictionary.WqxExample {
		if len(item[0]) > maxLen {
			maxLen = len(item[0])
		}
	}
	for _, item := range dictResponse.Dictionary.WqxExample {
		padding := maxLen - len(item[0])
		fmt.Printf("%s%s\t%s\n", item[0], strings.Repeat(" ", padding), item[1])
	}

5.输入操作

  • 命令行读取操作:命令行读取参数,命令行参数可以通过os.Args来获取。
func main() { 
    // 检查命令行参数数量是否为2 
    if len(os.Args) != 2 { 
        // 如果参数数量不正确,打印提示信息并退出程序 
        fmt.Fprintf(os.Stderr, `usage:simpleDict WORD example:simpleDict hello`) os.Exit(1) 
        } 
        // 获取命令行参数中的单词 
        word := os.Args[1] 
        // 调用函数查询彩云词典 
        queryCaiYun(word) 
        // 调用函数查询百度词典 
        queryBaiDu(word) }

打印结果 微信图片_20230728003345.png

  • 键盘读取操作:通过键盘输入来获取
func main() {
    // 声明一个变量来存储用户输入的单词
    var word string
    
    // 使用 fmt.Scanf 函数获取用户输入的单词,并将其存储到 word 变量中
    fmt.Scanf("%s", &word)

    // 调用函数查询彩云词典
    queryCaiYun(word)

    // 调用函数查询百度词典
    queryBaiDu(word)
}

打印结果 微信截图_20230728003505.png

「完整代码」

「参考文献」

Go 语言的实战案例|讲师: 王克纯