GO语言实战 在线词典 | 青训营笔记

101 阅读4分钟

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

课堂笔记

本堂课重点内容

  • 用GO语言来发送http请求解析JSON返回值
  • 使用代码生成提高开发效率

详细知识点介绍

  • 如何进行浏览器的包的抓取
  • 使用网站构建函数请求
  • 使用网站构建响应结构体
  • 灵活运用err判断寻找错误

实践练习例子

在线词典<命令行>

规则介绍

  • 输入单词
  • 输出音标、中文含义、词性.
  • 使用第三方的API进行查询

操作步骤

抓包
  • 彩云小译 - 在线翻译 (caiyunapp.com)

  • 在开发者视图中的网络框里进行数据流分析

  • 在翻译框里输入数据后点击翻译查看网络框的变化

  • 发现为dict的名称的POST请求

  • 在POST中的JSON字段发现

    • source表示待翻译单词
    • trans_type表示翻译的模式en2zh就是英文到中文
代码生成
  • 对dict进行复制curl

  • 在代码生成网站Convert curl commands to Go (curlconverter.com)进行GO语言代码生成

  • 将生成后的代码进行部分调整即可访问

    • 使用Client{}进行访问创建爱你
    • 使用NewRequest来构建流与请求(避免请求过大而导致内存爆开)
    • Header模拟电脑端的请求头
    • Do发起请求
    • 发起后使用err来进行错误判断
    • Close关闭请求
    • Readall读取响应(从流读取到内存里面)
    • 使用err再次进行错误判断
    • 打印响应
 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("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)
     }
     fmt.Printf("%s\n", bodyText)
 }
 ​
生成request body

构造一个结构体来进行阶层序列化

 type DictRequest struct {
     TransType string `json:"trans_type"`
     Source    string `json:"source"`
     UserID    string `json:"user_id"`
 }
解析response body
  • 在返回的字段这种找到解释、音标、词性的解答:explanations和prons
  • 和request的处理相同,构建一个结构体来进行反序列化
  • 使用代码生成网站JSON转Golang Struct - 在线工具 - OKTools
  • 由于这里不使用过多的操作,所以使用嵌套结构体
 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"`
         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"`
 }
 ​
打印结果
  • 解析响应得到的字符串并非全部都是我们需要的
  • 我们只需要一部分<字典>
  • 使用for循环遍历即可得到所需要的item
  • 注意:最好是在判定有无返回值后再进行状态码的判定,这样有助于检查问题
     fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
     for _, item := range dictResponse.Dictionary.Explanations {
         fmt.Println(item)
     }
完善代码
  • 将读返回值的代码封装为一个整体
  • 使用输入输出流来进行字符串读取,用户输入
  • 读取后进行判断单词并打印错误
 package main
 ​
 import (
     "bytes"
     "encoding/json"
     "fmt"
     "io/ioutil"
     "log"
     "net/http"
     "os"
 )
 ​
 type DictRequest struct {
     TransType string `json:"trans_type"`
     Source    string `json:"source"`
     UserID    string `json:"user_id"`
 }
 ​
 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"`
         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"`
 }
 ​
 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, `usage: simpleDict WORD
 example: simpleDict hello
         `)
         os.Exit(1)
     }
     word := os.Args[1]
     query(word)
 }
 ​

课后个人总结

  • 灵活运用判断进行错误的输出,方便调整整个函数
  • 使用多个网站在线进行构建请求和响应方便快捷