在线词典——我们可以在命令行输入一个单词,通过调用第三方API查询并打印该单词的释义。在这个小项目中我们可以接触到。这是在青训营学到的第一个实践小项目,作为一个入门的工程实践,让我们可以从中学习到go的web生态和http抓包,迅速从简单的小程序中走到web程序。
之前围观过python的爬虫抓包,但是go语言的http操作更简易,而且内置的json解析库、自动生成代码使得编写更为方便。
在这个小项目中我们可以接触到:
- 用Go语言发送HTTP请求
- 解析JSON文本
- 如何使用代码生成来提高开发效率
API准备
在线词典程序本质上是通过抓包,解析发送的http请求和收到的http响应。然后由在线词典程序将要搜索的单词放入请求里。填充好正确的请求后,发送到第三方词典网站api,就可以接收到想要的翻译结果请求了。
所以,在线词典程序的关键就是在于,正确解析请求和响应的json,放入搜索的单词和翻译。
以彩云科技的翻译网页为例,右键打开开发者工具
在搜索框中搜索“join”这个单词:
找到network中的dict包,并观察payload和preview:
负载
预览
生成请求resquest
通过开发者工具进行抓包发现,请求的header十分复杂,而我们又需要在代码中构造这个请求。所以我们可以使用一种代码自动生成器来生成这繁琐的代码。
header代码生成网址:header生成网站:curlconverter.com/#go
复制curl命令,在命令行运行可以收到一个json返回:
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: b4eac2e2e4f5357f738cc729f0f0e32f" ^
-H "origin: https://fanyi.caiyunapp.com" ^
-H "os-type: web" ^
-H "os-version;" ^
-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.1938.62" ^
-H "x-authorization: token:qgemv4jr1y38jyq6vhvi" ^
--data-raw "^{^\^"trans_type^\^":^\^"en2zh^\^",^\^"source^\^":^\^"join^\^"^}" ^
--compressed
通过header生成网站:curconverter.com/#go ,将以上json复制至该网站构造请求
生成请求头header:
运用工具自动生成代码的方法给了我很大的启发,突破了我曾经一个字一个字打的“笨方法”,对以后的工作提高效率有莫大的帮助。
其中,生成代码里做了:
- 创建HTTP客户端
client := &http.Client{}
- 创建请求体
var data = strings.NewReader(`{"trans_type":"en2zh","source":"join"}`)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
这是一个post请求,将会发送到第三方api上。我们可以这里面发现包含一个json请求体:{"trans_type":"en2zh","source":"join"}
其中,trans_type的值为en2zh,表示“英译中”;source的值为"join",即为我们要搜索的单词。通过这个json我们就可以搜索我们想要搜索的内容。
- 设置请求头
这一个步骤便是代码的繁琐之处了,用代码自动生成工具来辅助我们完成这一项繁琐的工作,帮我们减少了很多重复的劳动,大大提高工作效率。
- 发起请求,并接收响应到resp:
resp, err := client.Do(req)
- 读取响应:
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
请求头的命令行返回,成功运行之后就会获得返回的响应json:
go run zy_onlineDict
{"rc":0,"wiki":{},"dictionary":{"prons":{"en-us":"[d\u0292\u0254\u026an]","en":"[d\u0292\u0254in]"},"explanations":["vt.\u78b0\u5934;\u76f8\u4f1a;\u4ece\u4e8b;\u8fdb\u884c","vi.\u6210\u4e3a\u4f1a\u5458","n.\u63a5\u5408(\u70b9);\u5408\u6d41"],"synonym":["connect","fasten","unite","combine","couple"],"antonym":["separate","part","detach","divide","part"],"wqx_example":[["join up","\u8fde\u63a5\u8d77\u6765"],["join somebody in","\u548c\u67d0\u4eba\u4e00\u8d77\u2026"],["join hands","\u4f1a\u6218,\u5408\u4f5c,\u8054\u5408\u884c\u52a8"],["join forces","\u4f1a\u6218,\u5408\u4f5c,\u8054\u5408\u884c\u52a8"],["join ... with ...","\u628a\u2026\u8fde\u7ed3\u5728\u4e00\u8d77"],["join ... to ...","\u628a\u2026\u4e0e\u2026\u8054\u5408\u8d77\u6765"],["He'll join us for lunch . ","\u4ed6\u5c06\u548c\u6211\u4eec\u4e00\u8d77\u5403\u5348\u996d\u3002"]],"entry":"join","type":"word","related":[],"source":"wenquxing"}}
解析response body:
但是此时我们遇到了一些问题
- 发送的请求json是固定的,我们每次都要一个变量进行输入。
- 我们需要的只是其中的翻译内容,然而得到的响应json有许多内容,我们怎样才能高效获取我们所需的东西呢?
将json转为Struct
为了解决上述的两个问题,我们需要利用结构体来存放请求和响应json 这个结构体与请求响应字段一一对应。
但是与请求json不同的是,响应json明显更复杂。所以我们可以像请求头一样,借助自动生成代码来帮我们生成结构体。
利用该网站oktools.net/json2go 将json转为struct:
其中
DictRequest 结构体
DictResponse 结构体
这样我们就得到了一个response结构体,然后我们利用JSON.unmarshal把body反序列化到这个结构体里面,再试图打印出来:
err=json.Unmarshal(bodyText,&dictResponse)
if err!=nil{
log.Fatal(err)
}
fmt.Printf("%#v\n",dictResponse)
将所需要的字段选出来打印:
fmt.Println(word,"\n: ","UK",dictResponse.Dictionary.Prons.En,"US:",dictResponse.Dictionary.Prons.EnUs)
for _,item := range dictResponse.Dictionary.Explanations{
fmt.Println(item)
}
最后运行便可实现在线词典抓包,可以看见程序将我们所需的翻译结果打印了出来:
实践心得
这个小程序实践起来还是有一点点难度的,涉及到了http程序的许多重要基础,还有json解析反序列化等等操作。虽然有老师带着操作,但是其中很多原理我还是懵懵懂懂的,以后要找更多例子来实践,加深对抓包的理解。