本次我跟着课程实现了在线词典,我感觉是非常有意义的实践,真正让我体会到了自己写的代码具有实际功能,可以联网,让我感受颇深,收获很大,下面来从头记录一下过程。
抓包
本次我们使用的是彩云小译的api,网址在这里
在打开后,随便输个单词,然后按F12打开检查,找到网络一栏,这时候点击翻译,就可以发现它发送了很多请求,找到post请求,看payload,看到有音标的就是了。然后在复制里选择“全部复制为cURL (bash)”会复制到如下结果:
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 'cache-control: no-cache' \
-H 'content-type: application/json;charset=UTF-8' \
-H 'device-id: 30db560ce5d512463507563856510fbf' \
-H 'origin: https://fanyi.caiyunapp.com' \
-H 'os-type: web' \
-H 'os-version;' \
-H 'pragma: no-cache' \
-H 'referer: https://fanyi.caiyunapp.com/' \
-H 'sec-ch-ua: "Chromium";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"' \
-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/116.0.0.0 Safari/537.36 Edg/116.0.0.0' \
-H 'x-authorization: token:qgemv4jr1y38jyq6vhvi' \
--data-raw '{"trans_type":"en2zh","source":"good"}' \
--compressed
生成http请求
然后为了方便直接操作,我们就进入代码生成网站,把复制的内容粘进去,就能把它转换成Go语言的http请求,大概这样:
package main
import (
"fmt"
"io"
"log"
"net/http"
"strings"
)
func main() {
client := &http.Client{}
var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
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("cache-control", "no-cache")
req.Header.Set("content-type", "application/json;charset=UTF-8")
req.Header.Set("device-id", "30db560ce5d512463507563856510fbf")
req.Header.Set("origin", "https://fanyi.caiyunapp.com")
req.Header.Set("os-type", "web")
req.Header.Set("os-version", "")
req.Header.Set("pragma", "no-cache")
req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("sec-ch-ua", `"Chromium";v="116", "Not)A;Brand";v="24", "Microsoft Edge";v="116"`)
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/116.0.0.0 Safari/537.36 Edg/116.0.0.0")
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)
}
fmt.Printf("%s\n", bodyText)
}
下面来介绍下这段代码
首先第 12 行我们创建了一个 HTTP client,创建的时候可以指定很多参数,包括比如请求的超时是否使用 cookie 等。接下来是构造一个 HTTP 请求,这是一个 post 请求,然后会用到 HTTP .NewRequest ,第一个参数是 http 方法 POST, 第二个参数是 URL, 最后一个参数是 body ,body因为可能很大,为了支持流式发送,是一个只读流。我们用了 strings.NewReader 来把字符串转换成一个流。这样我们就成功构造了一个 HTTP request ,接下来我们需要对这个 HTTP request 来设置一堆 header。 接下来我们把我们调用 client.do request ,就能得到 response 如果请求失败的话,那么这个 error 会返回非 nil,会打印错误并且退出进程。response 有它的 HTTP 状态码, response header和body。 body同样是一个流,在golang里面,为了避免资源泄露,你需要加一个 defer 来手动关闭这个流,这个 defer 会在这个函数运行结束之后去执行。接下来我们是用 ioutil.ReadAll 来读取这个流,能得到整个body。我们再用 print 打印出来。
现在其实就可以运行了,不过直接运行会输出一个很难以理解的结果:
{"rc":0,"wiki":{},"dictionary":{"prons":{"en-us":"[g\u028ad]","en":"[gud]"},"explanations":["a.\u597d\u7684;\u5584\u826f\u7684;\u5feb\u4e50\u7684;\u771f\u6b63\u7684;\u5bbd\u5927\u7684;\u6709\u76ca\u7684;\u8001\u7ec3\u7684;\u5e78\u798f\u7684;\u5fe0\u5b9e\u7684;\u4f18\u79c0\u7684;\u5b8c\u6574\u7684;\u5f7b\u5e95\u7684;\u4e30\u5bcc\u7684","n.\u5229\u76ca;\u597d\u5904;\u5584\u826f;\u597d\u4eba","ad.=well"],"synonym":["excellent","fine","nice","splendid","proper"],"antonym":["bad","wrong","evil","harmful","poor"],"wqx_example":[["to the good","\u6709\u5229,\u6709\u597d\u5904"],["good, bad and indifferent","\u597d\u7684,\u574f\u7684\u548c\u4e00\u822c\u7684"],["good innings","\u957f\u5bff"],["good and ...","\u5f88,\u9887;\u5b8c\u5168,\u5f7b\u5e95"],["do somebody's heart good","\u5bf9\u67d0\u4eba\u7684\u5fc3\u810f\u6709\u76ca,\u4f7f\u67d0\u4eba\u611f\u5230\u6109\u5feb"],["do somebody good","\u5bf9\u67d0\u4eba\u6709\u76ca"],["be good for","\u5bf9\u2026\u6709\u6548,\u9002\u5408,\u80dc\u4efb"],["be good at","\u5728\u2026\u65b9\u9762(\u5b66\u5f97,\u505a\u5f97)\u597d;\u5584\u4e8e"],["as good as one's word","\u4fe1\u5b88\u8bfa\u8a00,\u503c\u5f97\u4fe1\u8d56"],["as good as","\u5b9e\u9645\u4e0a,\u51e0\u4e4e\u7b49\u4e8e"],["all well and good","\u4e5f\u597d,\u8fd8\u597d,\u5f88\u4e0d\u9519"],["a good","\u76f8\u5f53,\u8db3\u8db3"],["He is good at figures . ","\u4ed6\u5584\u4e8e\u8ba1\u7b97\u3002"]],"entry":"good","type":"word","related":[],"source":"wenquxing"}}
我们需要用到JSON序列化,我们需要构造一个相应的结构体,把返回的 JSON 反序列化到结构体里面,但是手动构造非常繁琐,下面再用到代码生成就可以了。
生成结构体
下面先回到检查那一步,把http请求的结果复制下来,大概这样
{
"rc": 0,
"wiki": {},
"dictionary": {
"prons": {
"en-us": "[g\u028ad]",
"en": "[gud]"
},
"explanations": [
"a.\u597d\u7684;\u5584\u826f\u7684;\u5feb\u4e50\u7684;\u771f\u6b63\u7684;\u5bbd\u5927\u7684;\u6709\u76ca\u7684;\u8001\u7ec3\u7684;\u5e78\u798f\u7684;\u5fe0\u5b9e\u7684;\u4f18\u79c0\u7684;\u5b8c\u6574\u7684;\u5f7b\u5e95\u7684;\u4e30\u5bcc\u7684",
"n.\u5229\u76ca;\u597d\u5904;\u5584\u826f;\u597d\u4eba",
"ad.=well"
],
"synonym": [
"excellent",
"fine",
"nice",
"splendid",
"proper"
],
"antonym": [
"bad",
"wrong",
"evil",
"harmful",
"poor"
],
"wqx_example": [
[
"to the good",
"\u6709\u5229,\u6709\u597d\u5904"
],
[
"good, bad and indifferent",
"\u597d\u7684,\u574f\u7684\u548c\u4e00\u822c\u7684"
],
[
"good innings",
"\u957f\u5bff"
],
[
"good and ...",
"\u5f88,\u9887;\u5b8c\u5168,\u5f7b\u5e95"
],
[
"do somebody's heart good",
"\u5bf9\u67d0\u4eba\u7684\u5fc3\u810f\u6709\u76ca,\u4f7f\u67d0\u4eba\u611f\u5230\u6109\u5feb"
],
[
"do somebody good",
"\u5bf9\u67d0\u4eba\u6709\u76ca"
],
[
"be good for",
"\u5bf9\u2026\u6709\u6548,\u9002\u5408,\u80dc\u4efb"
],
[
"be good at",
"\u5728\u2026\u65b9\u9762(\u5b66\u5f97,\u505a\u5f97)\u597d;\u5584\u4e8e"
],
[
"as good as one's word",
"\u4fe1\u5b88\u8bfa\u8a00,\u503c\u5f97\u4fe1\u8d56"
],
[
"as good as",
"\u5b9e\u9645\u4e0a,\u51e0\u4e4e\u7b49\u4e8e"
],
[
"all well and good",
"\u4e5f\u597d,\u8fd8\u597d,\u5f88\u4e0d\u9519"
],
[
"a good",
"\u76f8\u5f53,\u8db3\u8db3"
],
[
"He is good at figures . ",
"\u4ed6\u5584\u4e8e\u8ba1\u7b97\u3002"
]
],
"entry": "good",
"type": "word",
"related": [],
"source": "wenquxing"
}
}
然后来到这个网站,粘贴进去,就会生成Go的结构体了:
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 []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"`
}
结果反序列化到结构体里
然后我们就可以用 JSON.unmarshal 把 body 反序列化到 这个结构体里面,再试图打印出来
fmt.Printf("%s\n",bodyText)
换成
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n", dictResponse)
这样就可以输出的比较易读了,但是还是有很多不需要的内容,我们只需要音标和翻译就行,而且这只能输出我们最先输入的固定单词,我们想输入自己的单词该怎么办呢
格式化输出,添加用户输入
其实只要修改这里,把good换成想翻译的字符串就行了
strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
那么就把原来的main函数变成个普通函数,读入一个要翻译的单词,返回翻译结果,然后main读入要翻译的单词,调用此函数就大功告成了,下面就是完整的代码
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
}
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"`
}
func query(word string) {
client := &http.Client{}
request := DictRequest{TransType: "en2zh", Source: word}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
data := bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("content-type", "application/json;charset=UTF-8")
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 resp.StatusCode != 200 {
log.Fatal("bad Code:", resp.StatusCode, "body:", string(bodyText))
}
if err != nil {
log.Fatal(err)
}
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
fmt.Println(item)
}
}
func main() {
fmt.Println("请输入你想翻译的单词")
var word string
fmt.Scanln(&word)
fmt.Println(word)
query(word)
}