这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记,今天差不多半天都在整这个,可算是弄明白了
一.彩云小译版本
1.抓包
如果是第一次见到这个次,那么你一定会想问:什么是抓包? 百度百科是这样解释的:
抓包(packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。 了解这个概念之后,现在我们来进行实操,首先打开彩云小译在线翻译,进入网页后,在页面中点击右键,点击检查查看页面元素,然后点击network显示网络面板,如果你也如我一样第一次接触这个东西,看到这个面板之后可能会很懵,在进行我们抓包操作之前,我们先来详细介绍下这个面板
一共五部分,首先介绍一下控制器
第二,过滤器,在搜索框中输入关键词,请求列表会过滤只显示响应请求,也可以点击右侧关键词进行过滤
第三,在概览中可以框出时间来查看响应时间内的请求
第四,请求列表,最上面是一些字段名
- Name:资源的名称
- Status:HTTP状态码
- Type:请求的资源的MIME类型
- Initiator:发起请求的对象或进程。它可能有以下几种值:
- Size:服务器返回的响应大小(包括头部和包体),可显示解压后大小
- Time:总持续时间,从请求的开始到接受响应中的最后一个字节
- Waterfall:各请求相关活动的直观分析图 单击一个请求,我们会看到如下画面
从图上可以看到如下信息:
查看头部,包括请求头,响应头
预览响应正文:查看图像用
查看响应正文
发起请求的对象或进程
时间详细分布
第五,概要,统计了请求的数量以及流量
接下来,进行我们的操作,在翻译框中输入一个单词点击翻译,我们会看到出现dict请求,右键单击请求,以cURL命令形式复制该请求,进入Convert curl commands to code,生成GO语言代码
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("Accept", "application/json, text/plain, */*")
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6")
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", "cross-site")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("app-name", "xy")
req.Header.Set("os-type", "web")
req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`)
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 := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", bodyText)
}
2.修改代码--添加结构体
我们再回到刚才的彩云小译页面,右键点击Request Method为post的dict,复制其response,进入json转Golang结构体,嵌套生成我们需要的结构体
type AutoGenerated 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"`
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"`
}
大致结构已经完整了,接下来我们开始修改代码 首先添加DictRequest结构体
type DictRequest struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
然后将我们刚才生成的结构体名字改为DictResponse 在看到我们第一步抓包时生成的代码,改名为query方法 在该方法中,创建一个请求实例,编码成json格式,并转化为io流
request := DictRequest{TransType: "en2zh",Source: word}//创建一个实例,word为我们要查的单词
buf,err := json.Marshal(request)//将该请求实例解析为json格式
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf)//将我们的实例转化为ioReader流
其次,如果服务器处理了该请求会返回状态码200,将响应内容解码创建一个dictResponse实例,并输出我们想要的内容
if resp.StatusCode != 200 {
log.Fatal("bad status code:", resp.StatusCode, "body", string(bodyText))
}
//声明一个响应结构体并把响应体通过json解码初始化
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)
}
3.main函数实现
命令行参数,os.Args[0]为程序路径,os.Arg[1]为我们要查的单词
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)
}
嘿嘿嘿,经过这么一番折腾,看到运行结果出来的我感觉一下都释然了,如下;
二.有道智云版本
进入有道智云,输入一个单词翻译,需要注意的是,这里我们的是名为trans的请求,大致步骤同第一个版本一样,复制其cURL命令,转化成查询代码,复制response转化为响应结构体,值得注意的是,该版本不需要声明请求结构体,而是直接将word格式化为json格式输出作为创建流的参数
client := &http.Client{}
var data = strings.NewReader(fmt.Sprintf(`q=%s&from=en&to=zh-CHS`, word))
req, err := http.NewRequest("POST", "https://aidemo.youdao.com/trans", data)
if err != nil {
log.Fatal(err)
}
三.实现并行请求两个翻译引擎来提高响应速度
合并上面两个版本,主要修改main函数,通过waitgroup优雅地实现阻塞,开启两个协程,完整代码如下
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"sync"
)
type DictRequestCaiYun struct {
TransType string `json:"trans_type"`
Source string `json:"source"`
UserID string `json:"user_id"`
}
type DictResponseCaiYun 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"`
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"`
}
type DictResponseYouDao struct {
ReturnPhrase []string `json:"returnPhrase"`
Query string `json:"query"`
ErrorCode string `json:"errorCode"`
L string `json:"l"`
TSpeakURL string `json:"tSpeakUrl"`
Web []struct {
Value []string `json:"value"`
Key string `json:"key"`
} `json:"web"`
RequestID string `json:"requestId"`
Translation []string `json:"translation"`
Dict struct {
URL string `json:"url"`
} `json:"dict"`
Webdict struct {
URL string `json:"url"`
} `json:"webdict"`
Basic struct {
ExamType []string `json:"exam_type"`
UsPhonetic string `json:"us-phonetic"`
Phonetic string `json:"phonetic"`
UkPhonetic string `json:"uk-phonetic"`
Wfs []struct {
Wf struct {
Name string `json:"name"`
Value string `json:"value"`
} `json:"wf"`
} `json:"wfs"`
UkSpeech string `json:"uk-speech"`
Explains []string `json:"explains"`
UsSpeech string `json:"us-speech"`
} `json:"basic"`
IsWord bool `json:"isWord"`
SpeakURL string `json:"speakUrl"`
}
func queryByCaiYun(word string) {
client := &http.Client{} //创建一个客户端
request := DictRequestCaiYun{TransType: "en2zh", Source: word} //创建一个实例
buf, err := json.Marshal(request) //将该请求实例解析为json格式
if err != nil {
log.Fatal(err)
}
var data = bytes.NewReader(buf) //将我们的实例转化为ioReader流
req, err := http.NewRequest("POST",
"https://api.interpreter.caiyunai.com/v1/dict",
data) //向该网址以post方式发出我们的实例请求
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,en-GB;q=0.8,en-US;q=0.7,en;q=0.6")
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", "cross-site")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36")
req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
req.Header.Set("app-name", "xy")
req.Header.Set("os-type", "web")
req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`)
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 := ioutil.ReadAll(resp.Body) //读取响应体中的全部内容并以字节数组形式返回
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != 200 {
log.Fatal("bad status code:", resp.StatusCode, "body", string(bodyText))
}
//声明一个响应结构体并把响应体通过json解码初始化
var dictResponse DictResponseCaiYun
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 queryByYouDao(word string) {
client := &http.Client{}
var data = strings.NewReader(fmt.Sprintf(`q=%s&from=en&to=zh-CHS`, word))
req, err := http.NewRequest("POST", "https://aidemo.youdao.com/trans", data)
if err != nil {
log.Fatal(err)
}
req.Header.Set("authority", "aidemo.youdao.com")
req.Header.Set("accept", "application/json, text/javascript, */*; q=0.01")
req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6")
req.Header.Set("content-type", "application/x-www-form-urlencoded; charset=UTF-8")
req.Header.Set("origin", "https://ai.youdao.com")
req.Header.Set("referer", "https://ai.youdao.com/")
req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`)
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", "same-site")
req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36")
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 status code:", resp.StatusCode, "body", string(bodyText))
}
var dictResponse DictResponseYouDao
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
log.Fatal(err)
}
fmt.Println(word, "UK", dictResponse.Basic.UkPhonetic, "US", dictResponse.Basic.UsPhonetic)
for _, item := range dictResponse.Basic.Explains {
fmt.Println(item)
}
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
`)
os.Exit(1)
}
word := os.Args[1]
var wg sync.WaitGroup //维护一个计数器
wg.Add(2) //开两个协程,计数器为2
go func() {
defer wg.Done() //该协程结束,计数器减1
queryByCaiYun(word)
}()
go func() {
defer wg.Done()
queryByYouDao(word)
}()
wg.Wait() //等待计数器减为0,主函数即可结束
}