0.前言
本文通过对百度翻译和彩云翻译进行抓包实验,最终实现在控制台输入单词,输出翻译;为日后完成小组件开发打下基础。
1.流程
graph LR
A(开始) --> B(发送HTTP请求)
B --> C(接收HTTP响应)
C --> D(解析响应数据)
D --> E(提取翻译结果)
E --> F(输出翻译结果到控制台)
F --> G(结束)
2.实战-彩云翻译
- 打开浏览器控制台,调整到“网络”,将过滤条件设置为“dic”。
- 在网页中输入需要翻译的单词“extraordinary”并点击翻译按钮。
3.右键选择,以“cURL方式复制”
4.打开cURL转go代码粘贴,将生成的go代码复制到编辑器中
我们使用了Go语言的net/http包来构建HTTP请求。首先,我们创建一个http.Client对象,用于发送HTTP请求。然后,我们创建一个strings.NewReader对象,将需要翻译的文本作为请求的body数据。接下来,我们使用http.NewRequest函数创建一个POST请求,并设置请求的URL和headers。最后,我们使用client.Do方法发送请求,并通过io.ReadAll函数读取响应的body数据。最后,我们将翻译结果输出到控制台。
package main
import (
"fmt"
"io"
"log"
"net/http"
"strings"
)
func main() {
// 创建一个HTTP客户端
client := &http.Client{}
// 准备请求数据
var data = strings.NewReader(`{"trans_type":"en2zh","source":"extraordinary"}`)
// 创建POST请求
req, err := http.NewRequest("POST", "https://lingocloud.caiyunapp.com/v1/dict", data)
if err != nil {
log.Fatal(err)
}
// 设置请求头部信息
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Connection", "keep-alive")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-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")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("app-name", "xy")
req.Header.Set("device-id", "84821080de181d390056373e2b62e4ff")
req.Header.Set("os-type", "web")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"`)
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("sec-ch-ua-platform", `"Windows"`)
// 发送请求
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
//避免资源泄漏
defer resp.Body.Close()
// 读取响应body数据
bodyText, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
// 输出翻译结果到控制台
fmt.Printf("%s\n", bodyText)
}
4.分析结果
目前程序已经可以发出请求并返回JSON,下面我们用JSON序列化来将固定的输入更改为变化的。
5.定义与JSON相对应的结构体,用于序列化
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
}
6.借助JSON转GO工具解析response body完成反序列化
复制响应的JSON代码
将AutoGenerated修改为DictResponse
7.最终代码展示
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
// DictRequest 是查询词典的请求结构体
type DictRequest struct {
TransType string `json:"trans_type"` // 翻译类型
Source string `json:"source"` // 源语言
//UserID string `json:"user_id"` // 用户ID
}
// DictResponse 是查询词典的响应结构体
type DictResponse struct {
Rc int `json:"rc"` // 响应代码
Wiki struct {
KnownInLaguages int `json:"known_in_laguages"` // 已知语言数量
Description struct {
Source string `json:"source"` // 描述的源语言
Target interface{} `json:"target"` // 描述的目标语言
} `json:"description"` // 描述信息
ID string `json:"id"` // ID
Item struct {
Source string `json:"source"` // 词条的源语言
Target string `json:"target"` // 词条的目标语言
} `json:"item"` // 词条信息
ImageURL string `json:"image_url"` // 图片链接
IsSubject string `json:"is_subject"` // 是否为主题
Sitelink string `json:"sitelink"` // 网站链接
} `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"` // 词典信息
}
// query 函数发送查询请求并打印结果
func query(word string) {
client := &http.Client{}
// 创建查询请求
request := DictRequest{TransType: "en2zh", Source: word}
buf, err := json.Marshal(request)
if err != nil {
log.Fatal(err)
}
var 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("Connection", "keep-alive")
req.Header.Set("DNT", "1")
req.Header.Set("os-version", "")
req.Header.Set("sec-ch-ua-mobile", "?0")
req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
req.Header.Set("app-name", "xy")
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
req.Header.Set("Accept", "application/json, text/plain, */*")
req.Header.Set("device-id", "")
req.Header.Set("os-type", "web")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
req.Header.Set("Sec-Fetch-Site", "cross-site")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
// 发送请求并获取响应
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)
}
// 检查响应状态码
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
// 解析响应内容
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() {
// 检查命令行参数
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "请输入需要翻译的英语单词\n")
os.Exit(1)
}
// 获取查询的单词
word := os.Args[1]
// 发起查询
query(word)
}
运行效果展示:
错误代码:
3.实战-百度翻译
整体步骤同上。但需要注意的是,请求需要以"kw="开头。而JSON的键值对形式是“:”连接,所以果断放弃定义Request结构体。改用字符串拼接。
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
)
// DictResponse 定义了百度翻译API返回的数据结构
type DictResponse struct {
Data []struct {
V string `json:"V"`
} `json:"data"`
}
// query 是向百度翻译API发送查询的函数
func query(word string) {
client := &http.Client{}
// 构造请求体,将查询单词拼接到请求体中
//var prekey = "kw="
//prekey += word
//var data = strings.NewReader(prekey)
// 使用 strings.Builder 构建请求体字符串
var builder strings.Builder
builder.WriteString("kw=")
builder.WriteString(word)
data := strings.NewReader(builder.String())
// 创建POST请求
req, err := http.NewRequest("POST", "https://fanyi.baidu.com/sug", data)
if err != nil {
log.Fatal(err)
}
// 设置请求头部信息
req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01")
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("Connection", "keep-alive")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
req.Header.Set("Cookie", `BAIDUID=1076D050F2788A25103C13DAACFB429A:FG=1; BAIDUID_BFESS=1076D050F2788A25103C13DAACFB429A:FG=1; BAIDU_WISE_UID=wapp_1689869356701_44; ZFY=6H:AIM3EUIaby4LTJPS6eAMeF0xBddPKbEVBmGUaQAyg:C; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; RT="z=1&dm=baidu.com&si=cd51613b-0c14-4f14-8cde-6e0df1dc66af&ss=lkh0z7l3&sl=1&tt=19j&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=21x&ul=12qe&hd=12r1"; newlogin=1; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1690289124,1690422854,1690458316,1690512464; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1690514158; ab_sr=1.0.1_ZDU0ZmZhNThmMmE3YzM2YTZjNDBkMDlkNWRlMDNkNTI5YjliMDE1N2JjNTlkYzRhMmFiZmY2MmNkM2VkNjZlNWU0YWVkZWM5ZTNmNDhiNWYxMDllODY4OWNjMmFlMDllZWQxYjg0ZGYxMDk5OTU3MzI3NzA0ZDU0MTU3NTNjY2E0ODZkY2E3OTkzYjZkMWUxMmVmZDYxNDY0NDUxNzk4MQ==`)
req.Header.Set("Origin", "https://fanyi.baidu.com")
req.Header.Set("Referer", "https://fanyi.baidu.com/")
req.Header.Set("Sec-Fetch-Dest", "empty")
req.Header.Set("Sec-Fetch-Mode", "cors")
req.Header.Set("Sec-Fetch-Site", "same-origin")
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-Requested-With", "XMLHttpRequest")
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"`)
// 发送请求并获取响应
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)
}
// 检查响应状态码是否为200
if resp.StatusCode != 200 {
log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}
// 解析JSON响应
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
// 输出翻译结果
explain := dictResponse.Data[0].V
fmt.Println(word, explain)
}
func main() {
// 检查命令行参数是否符合要求
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, "请输入需要翻译的英语单词\n")
os.Exit(1)
}
word := os.Args[1]
query(word)
}
在上面给出两者字符串拼接方法,利用
strings.Builder
的高效性能,避免频繁地创建和拼接字符串。首先,我们实例化一个strings.Builder
对象,然后使用WriteString
方法逐个追加字符串片段,最后通过String
方法获取完整的字符串,并使用strings.NewReader
将其转换为io.Reader
类型的数据,用于设置请求体。这种方式比直接使用字符串拼接操作符(+)更高效。
运行效果展示:
4.改进方向
- 使用无条件for循环与break语句相搭配,实现连续翻译单词
- 改善字符串自命令行获取的输入方式,修改为程序运行后输入
- 增加对短语,句子的翻译支持
- 完善翻译类型,调用“自动检测语言”接口,实现中日韩英俄泰多种语言相互转化