ヾ(≧▽≦*)o参与「第六届青训营——后端 」的第 3 天
「项目简介」
- 该项目用Go实现一个在线词典青春版,能通过调用第三方的API查询到单词的翻译并打印
- 在项目实施的过程中,我学习了
网页抓包、代码生成、HTTP请求、JSON解析、获取命令行参数等操作 - 本项目实现了并行请求百度翻译、彩云翻译这两个翻译引擎提高响应速度
「实现步骤」
1.网页抓包
- 打开翻译网页(这边使用到的彩云翻译),网页右键选择
检查或按键盘F12打开网页控制台 - 输入需要查询的单词,点击翻译,浏览器会发送一段请求,在控制台上方选择网络(
Network),选中dict,注意请求方法为POST
- 这是一个HTTP的POST请求,请求头(Header)是一个JSON,里面有两个字段,代表要翻译的语言类型和所查询的单词。
- API的返回结果会有Wiki、dictionary两个字段。其中dictionary包含单词意思、音标、同义词以及例句等内容。
2.代码生成
- 需要在 Golang 里面去发送这个请求。但是这个请求比较复杂,用代码构造很麻烦。可以用一种简单的方式来生成代码,右键
dict,点击复制(copy)->复制cURL(bash)(copy as cURL)
curl(cmd)是使用命令行进行网络请求的一种方式,而curl(bash)是使用 Bash 脚本来执行curl命令的方式。![]()
得到内容如下
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
- 打开cURL命令转换代码网站,选择转换的代码为
Go,将转换后的代码复制到编译器
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.Do是http.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返回的结构非常复杂,如果要逐一定义结构体字段,将非常繁琐且容易出错。
- 因为
response返回的数据非常多而且复杂,自己书写起来很容易出错,所以我们可以通过JSON转结构体工具来生成代码。打开这个网站,把浏览器response的内容复制到网站里点击转换-嵌套就能生成对应的结构体。
代码如下
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) }
打印结果
- 键盘读取操作:通过键盘输入来获取
func main() {
// 声明一个变量来存储用户输入的单词
var word string
// 使用 fmt.Scanf 函数获取用户输入的单词,并将其存储到 word 变量中
fmt.Scanf("%s", &word)
// 调用函数查询彩云词典
queryCaiYun(word)
// 调用函数查询百度词典
queryBaiDu(word)
}
打印结果
「完整代码」