Go语言学习(二) 在线词典项目|青训营笔记

Go 语言学习(二) 在线词典项目_青训营笔记

  • Go 语言学习(二) 在线词典项目_青训营笔记
    • 一、在线词典项目介绍
    • 二、开发步骤
      • 1. 抓包
      • 2. 代码生成
      • 3. 生成 request body
      • 4. 解析 request body
      • 5. 打印结果
    • 三、代码展示
      • 1. 主函数
      • 2. 彩云翻译函数
      • 3. 百度翻译函数

这是我参与「第三届青训营 -后端场」笔记创作活动的的第 2 篇笔记。

一、在线词典项目介绍

用户可以在命令行里面查询一个单词。此程序能通过调用第三方的 API 查询到单词的翻译并打印出来。

在这个项目里面,我学习了如何用 go 语言来发送 HTTP 请求、解析 json,学习了如何使用代码生成来提高开发效率。

Go语言学习(二) 在线词典项目|青训营笔记_第1张图片

二、开发步骤

1. 抓包

  • 要用到的 API 是彩云科技提供的在线翻译。先打开彩云翻译的网页,然后右键检查打开浏览器的开发者工具。

Go语言学习(二) 在线词典项目|青训营笔记_第2张图片

  • 此时点击翻译按钮,浏览器会发送一系列请求,找到查询单词的请求。这是一个 HTTP 的 post 的请求,请求的 header 是一个 json,里面有两个字段,代表要翻译的语言和查询的单词。API 的返回结果里面会有 Wiki 和 dictionary 两个字段。需要用的结果主要在 dictionary.Explanations 字段里面,其他字段里面还包括音标等信息。

Go语言学习(二) 在线词典项目|青训营笔记_第3张图片

Go语言学习(二) 在线词典项目|青训营笔记_第4张图片

Go语言学习(二) 在线词典项目|青训营笔记_第5张图片

2. 代码生成

  • 需要在 Golang 里面去发送这个请求。但是这个请求比较复杂,用代码构造很麻烦。可以用一种简单的方式来生成代码,右键浏览器里面的 copy as curl,在终端粘贴一下 curl 命令,可以返回一大串 json。

Go语言学习(二) 在线词典项目|青训营笔记_第6张图片

Go语言学习(二) 在线词典项目|青训营笔记_第7张图片

  • 打开代码生成网站,粘贴 curl 请求,即可自动生成 Golang 代码。

Go语言学习(二) 在线词典项目|青训营笔记_第8张图片

3. 生成 request body

  • 在 Golang 里面,需要生成一段 JSON ,常用的方式是先构造出来一个结构体,这个结构体和需要生成的 JSON 的结构是一一对应的。

Go语言学习(二) 在线词典项目|青训营笔记_第9张图片

4. 解析 request body

  • 接下来把这个 response body 来解析出来。在 js/Python 这些脚本语言里面,body 是一个字典或者 map 的结构, 可以直接从里面取值,但是 golang 是个强类型语言,这种做法并不是最佳实践。更常用的方式是和 request 的一样,写一个结构体,把返回的 JSON 反序列化到结构体里面。但是在浏览器里面可以看到这个 API 返回的结构非常复杂,如果要一一定义结构体字段,非常繁琐并且容易出错。

Go语言学习(二) 在线词典项目|青训营笔记_第10张图片

  • 此时可以使用对应的代码生成工具 ,把 json 字符串粘贴进去,自动生成对应结构体。

Go语言学习(二) 在线词典项目|青训营笔记_第11张图片

5. 打印结果

  • 观察那个 json 可以看出需要的结果是在 Dictionary.explanations 里的,用 for range 循环来迭代它,然后直接打印结构,参照一些词典的显示方式,可以在前面打印出这个单词和它的音标。

Go语言学习(二) 在线词典项目|青训营笔记_第12张图片

三、代码展示

经过课后编程实践,我实现了并行请求两个翻译引擎来提高响应速度。

下面是我的代码展示,包含了代码详细注释,便于理解。

1. 主函数

package main

import (
   "fmt"
   "os"
   "sync"
   "time"
)

func main() { //此在线词典程序需要在 Git Bash终端命令行运行,如输入 go run *.go hello
   start := time.Now()
   if len(os.Args) != 2 { // Args 保存命令行参数,以程序名称开头,这里第2个参数为要查询的单词
      fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
example: simpleDict hello
      `) // Fprintf() 根据格式说明符格式化并写入 os.Stderr 。它返回写入的字节数和遇到的任何写入错误。
      os.Exit(1)
   }
   word := os.Args[1]

   wg := sync.WaitGroup{} // WaitGroup() 等待一组 goroutine 完成,主 goroutine 调用 Add() 来设置要等待的 goroutine 的数量,然后每个 goroutine 运行并在完成时调用Done(),同时, Wait()可以用来阻塞,直到所有的 goroutine 都完成
   wg.Add(2)              //将可能为负的增量添加到 WaitGroup() 计数器,如果计数器变为零,则所有在 Wait() 上阻塞的 goroutine 都会被释放,如果计数器变为负数,请添加错误处理
   go QueryCaiYun(word, &wg)
   go QueryBaiDu(word, &wg)

   wg.Wait()
   finish := time.Now()
   fmt.Println("runtime:", finish.Sub(start))
}

2. 彩云翻译函数

package main

import (
   "bytes"
   "encoding/json"
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
   "sync"
)

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

type CaiYunDictResponse 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 QueryCaiYun(word string, wg *sync.WaitGroup) {
   client := &http.Client{} //定义客户
   // var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`) //把输入的字符串 data 转换成流 req
   request := CaiYunDictRequest{TransType: "en2zh", Source: word}
   buf, err := json.Marshal(request) // Marshal() 返回 request 的 JSON 编码,序列化为一个 byte 数组。
   if err != nil {
      log.Fatal(err) //打印日志,退出程序
   }
   var data = bytes.NewReader(buf) //把输入的 byte 数组转换成流 req
   /* 创建请求 */
   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;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/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/100.0.4896.127 Safari/537.36 Edg/100.0.1185.44")
   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="100", "Microsoft Edge";v="100"`)
   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() //defer 会在函数结束后从后往前触发,Close() 手动关闭 Body流,防止内存资源泄露
   /*读取响应*/
   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)) //打印日志,退出程序
   }
   // fmt.Printf("%s\n", bodyText)
   var dictResponse CaiYunDictResponse
   err = json.Unmarshal(bodyText, &dictResponse) //Unmarshal()解析 bodyText的 JSON 编码的数据并反序列化将结果存储在 dictResponse 指向的值中。
   if err != nil {
      log.Fatal(err) //打印日志,退出程序
   }
   // fmt.Printf("%#v\n", dictResponse)
   fmt.Println("--------------------彩云翻译-----------------------")
   fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
   for _, item := range dictResponse.Dictionary.Explanations {
      fmt.Println(item)
   }

   wg.Done()
}

3. 百度翻译函数

package main

import (
   "bytes"
   "encoding/json"
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
   "sync"
)

type BaiDuDictRequest struct {
   Query  string `json:"query"`
   UserID string `json:"user_id"`
}

type BaiDuDictResponse struct {
   TransResult struct {
      Data []struct {
         Dst        string          `json:"dst"`
         PrefixWrap int             `json:"prefixWrap"`
         Result     [][]interface{} `json:"result"`
         Src        string          `json:"src"`
      } `json:"data"`
      From     string `json:"from"`
      Status   int    `json:"status"`
      To       string `json:"to"`
      Type     int    `json:"type"`
      Phonetic []struct {
         SrcStr string `json:"src_str"`
         TrgStr string `json:"trg_str"`
      } `json:"phonetic"`
   } `json:"trans_result"`
   DictResult struct {
      Edict struct {
         Item []struct {
            TrGroup []struct {
               Tr          []string      `json:"tr"`
               Example     []interface{} `json:"example"`
               SimilarWord []string      `json:"similar_word"`
            } `json:"tr_group"`
            Pos string `json:"pos"`
         } `json:"item"`
         Word string `json:"word"`
      } `json:"edict"`
      Collins struct {
         Entry []struct {
            EntryID string `json:"entry_id"`
            Type    string `json:"type"`
            Value   []struct {
               MeanType []struct {
                  InfoType string `json:"info_type"`
                  InfoID   string `json:"info_id"`
                  Example  []struct {
                     ExampleID string `json:"example_id"`
                     TtsSize   string `json:"tts_size"`
                     Tran      string `json:"tran"`
                     Ex        string `json:"ex"`
                     TtsMp3    string `json:"tts_mp3"`
                  } `json:"example"`
               } `json:"mean_type"`
               Gramarinfo []interface{} `json:"gramarinfo"`
               Tran       string        `json:"tran"`
               Def        string        `json:"def"`
               MeanID     string        `json:"mean_id"`
               Posp       []struct {
                  Label string `json:"label"`
               } `json:"posp"`
            } `json:"value"`
         } `json:"entry"`
         WordName      string `json:"word_name"`
         Frequence     string `json:"frequence"`
         WordEmphasize string `json:"word_emphasize"`
         WordID        string `json:"word_id"`
      } `json:"collins"`
      From        string `json:"from"`
      SimpleMeans struct {
         WordName  string   `json:"word_name"`
         From      string   `json:"from"`
         WordMeans []string `json:"word_means"`
         Exchange  struct {
            WordPl  []string `json:"word_pl"`
            WordEst []string `json:"word_est"`
            WordEr  []string `json:"word_er"`
         } `json:"exchange"`
         Tags struct {
            Core  []string `json:"core"`
            Other []string `json:"other"`
         } `json:"tags"`
         Symbols []struct {
            PhEn  string `json:"ph_en"`
            PhAm  string `json:"ph_am"`
            Parts []struct {
               Part  string   `json:"part"`
               Means []string `json:"means"`
            } `json:"parts"`
            PhOther string `json:"ph_other"`
         } `json:"symbols"`
      } `json:"simple_means"`
      Lang   string `json:"lang"`
      Oxford struct {
         Entry []struct {
            Tag  string `json:"tag"`
            Name string `json:"name"`
            Data []struct {
               Tag  string `json:"tag"`
               Data []struct {
                  Tag   string `json:"tag"`
                  P     string `json:"p"`
                  PText string `json:"p_text"`
               } `json:"data"`
            } `json:"data"`
         } `json:"entry"`
         Unbox []struct {
            Tag  string `json:"tag"`
            Type string `json:"type"`
            Name string `json:"name"`
            Data []struct {
               Tag     string   `json:"tag"`
               Text    string   `json:"text,omitempty"`
               Words   []string `json:"words,omitempty"`
               Outdent string   `json:"outdent,omitempty"`
               Data    []struct {
                  Tag    string `json:"tag"`
                  EnText string `json:"enText"`
                  ChText string `json:"chText"`
               } `json:"data,omitempty"`
            } `json:"data"`
         } `json:"unbox"`
      } `json:"oxford"`
      Sanyms []struct {
         Tit  string `json:"tit"`
         Type string `json:"type"`
         Data []struct {
            P string   `json:"p"`
            D []string `json:"d"`
         } `json:"data"`
      } `json:"sanyms"`
      Usecase struct {
         Idiom []struct {
            P    string `json:"p"`
            Tag  string `json:"tag"`
            Data []struct {
               Tag  string `json:"tag"`
               Data []struct {
                  EnText string `json:"enText"`
                  Tag    string `json:"tag"`
                  ChText string `json:"chText"`
                  Before []struct {
                     Tag  string `json:"tag"`
                     Data []struct {
                        EnText string `json:"enText"`
                        Tag    string `json:"tag"`
                        ChText string `json:"chText"`
                     } `json:"data"`
                  } `json:"before,omitempty"`
               } `json:"data"`
            } `json:"data"`
         } `json:"idiom"`
      } `json:"usecase"`
      BaiduPhrase []struct {
         Tit   []string `json:"tit"`
         Trans []string `json:"trans"`
      } `json:"baidu_phrase"`
      QueryExplainVideo struct {
         ID           int    `json:"id"`
         UserID       string `json:"user_id"`
         UserName     string `json:"user_name"`
         UserPic      string `json:"user_pic"`
         Query        string `json:"query"`
         Direction    string `json:"direction"`
         Type         string `json:"type"`
         Tag          string `json:"tag"`
         Detail       string `json:"detail"`
         Status       string `json:"status"`
         SearchType   string `json:"search_type"`
         FeedURL      string `json:"feed_url"`
         Likes        string `json:"likes"`
         Plays        string `json:"plays"`
         CreatedAt    string `json:"created_at"`
         UpdatedAt    string `json:"updated_at"`
         DuplicateID  string `json:"duplicate_id"`
         RejectReason string `json:"reject_reason"`
         CoverURL     string `json:"coverUrl"`
         VideoURL     string `json:"videoUrl"`
         ThumbURL     string `json:"thumbUrl"`
         VideoTime    string `json:"videoTime"`
         VideoType    string `json:"videoType"`
      } `json:"queryExplainVideo"`
   } `json:"dict_result"`
   LijuResult struct {
      Double string   `json:"double"`
      Tag    []string `json:"tag"`
      Single string   `json:"single"`
   } `json:"liju_result"`
   Logid int64 `json:"logid"`
}

func QueryBaiDu(word string, wg *sync.WaitGroup) {
   client := &http.Client{} //定义客户
   // var data = strings.NewReader(`query=good`) //把输入的字符串 data 转换成流 req
   request := BaiDuDictRequest{Query: word}
   buf, err := json.Marshal(request) // Marshal() 返回 request 的 JSON 编码,序列化为一个 byte 数组。
   if err != nil {
      log.Fatal(err) //打印日志,退出程序
   }
   var data = bytes.NewReader(buf) //把输入的 byte 数组转换成流 req
   /* 创建请求 */
   req, err := http.NewRequest("POST", "https://fanyi.baidu.com/langdetect", data)
   if err != nil {
      log.Fatal(err) //打印日志,退出程序
   }
   /* 设置请求头 */
   req.Header.Set("Accept", "*/*")
   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", `BIDUPSID=2860D678CCB10886990D4D819CC24BAB; PSTM=1603262095; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; __yjs_duid=1_3242d9ae5ec151bc4e7e9d908fe0e54c1620968286434; BDUSS=g3Tm93dWtraHZkRUdsdzlJeDEyalp2RmpZOW1RNmlDMUV3d2N0M0c4Z0pqNXhoRVFBQUFBJCQAAAAAAAAAAAEAAABffmtXxKnTsNChutpYSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkCdWEJAnVhfk; BDUSS_BFESS=g3Tm93dWtraHZkRUdsdzlJeDEyalp2RmpZOW1RNmlDMUV3d2N0M0c4Z0pqNXhoRVFBQUFBJCQAAAAAAAAAAAEAAABffmtXxKnTsNChutpYSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkCdWEJAnVhfk; APPGUIDE_10_0_2=1; BAIDUID=3A21E0D3A023E0BF48D5E6AB59882FC3:SL=0:NR=20:FG=1; MAWEBCUID=web_ZbMGPxbtNTJgOhwuypnUUKjyVeChjrWNsCSTdnAIkJyWEtWEYb; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1652153403,1652175540,1652186130,1652278875; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1652506784; delPer=0; PSINO=1; BAIDUID_BFESS=3A21E0D3A023E0BF48D5E6AB59882FC3:SL=0:NR=20:FG=1; RT="z=1&dm=baidu.com&si=hqolag82j09&ss=l35hyf6s&sl=2&tt=56c&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=5ne&ul=2axq&hd=2b02"; BA_HECTOR=24052g04012lag8kt41h7ulf10q; BDRCVFR[OEHfjv-pq1f]=mk3SLVN4HKm; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; H_PS_PSSID=`)
   req.Header.Set("Origin", "https://fanyi.baidu.com")
   req.Header.Set("Referer", "https://fanyi.baidu.com/?aldtype=16047")
   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/100.0.4896.127 Safari/537.36 Edg/100.0.1185.44")
   req.Header.Set("X-Requested-With", "XMLHttpRequest")
   req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="100", "Microsoft Edge";v="100"`)
   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() //defer 会在函数结束后从后往前触发,Close() 手动关闭 Body流,防止内存资源泄露
   /*读取响应*/
   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)) //打印日志,退出程序
   }
   // fmt.Printf("%s\n", bodyText)
   var dictResponse BaiDuDictResponse
   err = json.Unmarshal(bodyText, &dictResponse) //Unmarshal()解析 bodyText的 JSON 编码的数据并反序列化将结果存储在 dictResponse 指向的值中。
   if err != nil {
      log.Fatal(err) //打印日志,退出程序
   }
   // fmt.Printf("%#v\n", dictResponse)
   fmt.Println("--------------------百度翻译-----------------------")
   fmt.Println(word, "UK:", dictResponse.DictResult.SimpleMeans.Symbols, "US:", dictResponse.DictResult.SimpleMeans.Symbols)
   for _, item := range dictResponse.DictResult.SimpleMeans.WordMeans {
      fmt.Println(item)
   }

   wg.Done()
}

你可能感兴趣的:(学习笔记,golang,后端)