这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
1 介绍
- 功能:用户可以在命令行输入想要查询的单词,然后程序会通过调用第三方翻译的API查询单词的翻译内容,并将查询结果打印到终端显示出来
- 技术要点:Go、HTTP请求的发送和响应的处理、解析JSON
2 实现过程
2.1 选择翻译引擎
在选择第三方翻译引擎时,需要选择没有反抓包功能的翻译引擎,其次最好选择没有对翻译请求信息进行加密的翻译引擎,目前可以满足这些要求的翻译引擎主要有彩云翻译、火山翻译等,这里选择了火山翻译引擎:translate.volcengine.com/translate
2.2 抓包
首先打开火山翻译引擎的网页,然后按F12或点击鼠标右键“检查”打开开发者工具:
- 点击开发者工具选项中的
Network - 在翻译网站的输入框中输入要查询的单词
- 在Name框中查找翻译引擎响应的含有翻译内容的文件
- 具体查找方法为,点击Preview选项,找到响应内容中含有可以进行处理的翻译内容的文件
2.3 代码生成
接下来需要使用Golang发送这个翻译单词的请求,这一步可以借助往网站:curlconverter.com/#go 完成:
在响应文件上点击鼠标右键,找到Copy as cURL或Copy as cURL(bash)选项,然后将复制内容粘贴到代码生成网站上面的输入框,将下面生成的代码复制到本地的代码文件中,这里将这个代码文件命名为main.go:
运行程序,可以看到,已经可以获取相应的响应,还需对响应内容进行进一步的格式处理:
2.4 生成request body
之前完成的是对good这个特定单词的请求构造,如果要查询任意一个单词,那么需要根据单词构造相应的请求结构,首先点击开发者工具中的Headers选项,查看good单词查询请求的Request Payload:
可以看到有两个字段,text代表要查询的单词,language代表查询单词的语言,那么可以构造相应的请求结构体:
type DictRequest struct {
Text string `json:"text"`
Language string `json:"language"`
}
将原来主函数中var data = strings.NewReader({"text":"good","language":"en"})这行语句,改为:
request := DictRequest{Text: "good", Language: "en"}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
初始化DictRequest请求结构体后,使用json.Marshal方法获取序列化后的字符串,然后再发送查询请求,这样就构造了一个request body来存储需要查询的单词,这一步与之前一步的结果应该是一样的。
2.5 解析response body
此时得到的响应内容是JSON格式的,还需要进行进一步的处理,将response body解析出来,具体做法是构建一个结构体,将返回的JSON格式的内容反序列化到这个结构体中,然后再对内容进行解析,可以借助网站:oktools.net/json2go 进行结构体的构建:
- 点击开发者工具选项中的
Response,复制其中的内容 - 在转换网站的左侧框中粘贴复制的内容
- 点击“转换-嵌套”按钮
- 将右侧框中的转换得到的response结构体复制到
main.go中,并将结构体名改为DictResponse
接下来将之前读取的响应内容,使用json.Unmarshal方法反序列化到响应结构体中,即将主函数最后的fmt.Printf("%s\n", bodyText)这行语句,修改变成:
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
此时响应内容以JSON格式存储在了dictResponse中。
2.6 打印结果
因为要查询任意单词,所以将之前的主函数改为一个参数为单词字符串的查询函数,即将func main()改为func query(word string),将查询特定good单词的语句request := DictRequest{Text: "good", Language: "en"}改为request := DictRequest{Text: word, Language: "en"},并添加主函数:
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD example: simpleDict hello`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}
此时在命令行下进入文件目录后输入go run main.go word,即可对指定单词进行查询
最后在query方法中添加对响应是否成功的判断,并对响应内容存储进的JSON结构体进行分析,更好地输出需要的翻译内容,比如可以看到:
英式发音的音标所处的位置为dictResponse.Words[0].PosList[0].Phonetics[0].Text,美式发音的音标所处的位置为dictResponse.Words[0].PosList[0].Phonetics[1].Text,而需要的翻译结果在dictResponse.Words[0].PosList中,利用range对其进行遍历,打印每一项的Explanations[0].Text,即可获得翻译内容,最后代码:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type DictRequest struct {
Text string `json:"text"`
Language string `json:"language"`
}
type DictResponse struct {
Words []struct {
Source int `json:"source"`
Text string `json:"text"`
PosList []struct {
Type int `json:"type"`
Phonetics []struct {
Type int `json:"type"`
Text string `json:"text"`
} `json:"phonetics"`
Explanations []struct {
Text string `json:"text"`
Examples []struct {
Type int `json:"type"`
Sentences []struct {
Text string `json:"text"`
TransText string `json:"trans_text"`
} `json:"sentences"`
} `json:"examples"`
Synonyms []interface{} `json:"synonyms"`
} `json:"explanations"`
Relevancys []interface{} `json:"relevancys"`
} `json:"pos_list"`
} `json:"words"`
Phrases []interface{} `json:"phrases"`
BaseResp struct {
StatusCode int `json:"status_code"`
StatusMessage string `json:"status_message"`
} `json:"base_resp"`
}
func query(word string) {
// 创建客户端
client := &http.Client{}
request := DictRequest{Text: word, Language: "en"}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)
// 创建请求
req, err := http.NewRequest("POST", "https://translate.volcengine.com/web/dict/match/v1/?msToken=&X-Bogus=DFSzswVLQDc7EEGDSWt7ldCr9FuZ&_signature=_02B4Z6wo00001l4QzPQAAIDBlqwF6vei3lJeEMhAAPYOuzarBrmXaa6uYwNRblftdnJ6RWiBMA2AMxALHB6M3g4C9XW-JqScAK.Vq-5KZWzKetsthBs8vr-AM201Y9KO7PpDzFI2FYqvx9lJ88", data)
if err != nil {
log.Fatal(err)
}
// 设置请求头
req.Header.Set("authority", "translate.volcengine.com")
req.Header.Set("sec-ch-ua", `"Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"`)
req.Header.Set("accept", "application/json, text/plain, */*")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
req.Header.Set("content-type", "application/json")
req.Header.Set("origin", "https://translate.volcengine.com")
req.Header.Set("sec-fetch-site", "same-origin")
req.Header.Set("sec-fetch-mode", "cors")
req.Header.Set("sec-fetch-dest", "empty")
req.Header.Set("referer", "https://translate.volcengine.com/translate?category=&home_language=zh&source_language=detect&target_language=zh&text=good")
req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
// 发起请求,并获取响应
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
// 读取响应内容
bodyText, err := ioutil.ReadAll(resp.Body)
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.Words[0].PosList[0].Phonetics[0].Text, "US:", dictResponse.Words[0].PosList[0].Phonetics[1].Text)
for i, item := range dictResponse.Words[0].PosList {
fmt.Println(i+1, item.Explanations[0].Text)
}
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD example: simpleDict hello`)
os.Exit(1)
}
word := os.Args[1]
query(word)
}
打印结果:
3 个人思考
在入门了Go语言,了解了一些Go语言的基本语法后,尝试进行了一次项目的实战。这个项目整体并不算复杂,并使用了一些网站工具辅助完成,使我对Go语言进行网络编程的过程有了更深的了解,使用Go语言的能力也有了很大的提高。