Go实战案例二 | 青训营笔记

85 阅读3分钟

这是我参与「第五届青训营」笔记创作活动的第4天!

案例二 在线词典——抓包

案例题目

功能:实现在命令行输入一个单词,输出该单词相关的音标和翻译

原理:调用第三方的API对单词进行翻译并打印出来

思路分析

1. 抓包

以彩云小译为例,进行抓包操作 在翻译时检查页面,触发了Post请求

image.png

image.png 对dict中的负载进行分析,有要翻译的单词good,en2zh说明是英文翻译到中文 image.png dict中的预览则包含了更多关于good的音标和翻译 image.png

2. 代码生成

通过复制dict的cURL得到该curl命令 在终端中打开如下:

image.png

打开curlconverter.com/#go 将刚才得到的curl命令粘贴到其中,选择Go并生成代码

image.png

对该生成代码进行分析

package main

import (
	"fmt"
	"io/ioutil"
	"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("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "")
	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="109", "Chromium";v="109"`)
	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/109.0.0.0 Safari/537.36 Edg/109.0.1518.61")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	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)
	}
	fmt.Printf("%s\n", bodyText)
}

运行后生成相应的JSON

image.png

3. 生成request body

定义DictRequest结构体

type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

代码部分修改

client := &http.Client{}
request := DictRequest{TransType: "en2zh", Source: "good"} //初始化结构体变量
buf, err := json.Marshal(request)                          //将request序列化成数组
if err != nil {
	log.Fatal(err)
}
var data = bytes.NewReader(buf) //修改返回类型为byte数组,非字符串形式
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
	log.Fatal(err)
            
}

4. 解析response body

通过oktools.net/json2go

对preview中的JSON进行结构体生成 image.png

对源代码部分进行修改

var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse) //反序列化
if err != nil {
	log.Fatal(err)
}
fmt.Printf("%#v\n", dictResponse)

5. 打印结果和代码修改

对原打印代码dictResponse进行修改获得想要的翻译信息

fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
	fmt.Println(item)

添加一个序列码的检验,以防止出现空结果时纠错

if resp.StatusCode != 200 {
	log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}

6.代码完善

func main() {
	//错误处理,若判断os.Args不是两个则打印错误信息
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
		`)
		os.Exit(1)
	}
	//若正确则调用query即可
	word := os.Args[1]
	query(word)
}

执行效果如下

image.png

总结

这次的项目是用Go语言实现抓包,整体来说比较复杂。不仅需要对前端网页的布局有一定认识,也要对后端整体的结构体和代码进行新的布局和修改。同时也运用了一些外部网站对curl命令进行代码生成,对JSON进行结构体生成等。在对代码进行修改时,应注意适当的添加一些错误处理检验部分,优化代码的完整性。