在线词典|青训营笔记

130 阅读6分钟

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

Go语言的简介

Google出品的通用性编程语言

  1. 高性能、高并发

  2. 语法简单、学习曲线平缓

  3. 丰富的标准库

  4. 完善的工具链:编译、代码格式化、代码提示、单元测试等

  5. 静态链接:编译结构默认都是静态链接的,只需要拷贝编译之后的可执行文件就能部署运行,体积小,部署快捷

  6. 快速编译

  7. 跨平台:能在常见的平台运行,也能开发Android和IOS,路由器、树莓派上也能跑

  8. 垃圾回收

    //简单实现http服务器
    package main 
    import(
      "net/http"
    )
    
    func main() {
      //将路由/指向处理的文件
      http.Handle("/",http.FileServer(http.Dir(".")))
      //增添8080端口,并启动服务器
      http.ListenAndServe(":8080",nil)
    }
    

Go语言入门

开发环境

  1. 安装Go语言

    • 相关下载网址:

    • 开启go module

      • Windows

        go env -w GO111MODULE=on
        
      • Linux或macOS

        export GO111MODULE=on
        
    • 配置goproxy:

      • 阿里云配置:

        • Windows下:

          go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/
          
        • Linux或macOS下:

          export GOPROXY=https://mirrors.aliyun.com/goproxy/
          
      • 七牛云配置:

        • Windows下:

          go env -w GOPROXY=https://goproxy.cn,direct
          
        • Linux或macOS

          export GOPROXY=https://goproxy.cn,direct
          
        • goland当中直接配置也可以

          • File→setting→Go Modules

            image_Wt9eDW_xMN.png

  2. 配置集成开发环境

    1. vscode
    2. goland

基础语法和标准库

这里设计的大多都是包的使用,就记录个别示例

  1. 字符串格式化

    //拿结构体来举个例子,伪代码
    type point struct {
      x, y int
    }
    p := point{1, 2}
    fmt.Printfln(p)        //{1 2}
    fmt.Printf("p=%v\n",p) //{p={1 2}
    fmt.Printf("p=%+v\n",p)//p={x:1 y:2}
    fmt.Printf("p=%#v\n",p)//p=main.point{x:1, y:2}
    
  2. Json字段

    image_fp3s3GyB86.png

Go语言实战

猜谜游戏

一个之前学C的时候做过的小游戏,逻辑比较简单,主要是有以下几点:

  1. bufio.NewReader()和fmt.Scanf()中读取输入的区别
  2. 使用math/rand包中的rand.Intn()方法之前,需要设置随机数种子,可以用时间戳设置

代码:

package main

import (
  "fmt"
  "math/rand"
  "time"
)

func main() {
  maxNum := 100
  //如果不加随机数种子,那么每次生成的随机数会是同一个
  //这里用启动时的时间戳来生成随机数种子
  rand.Seed(time.Now().UnixNano())
  secretNumber := rand.Intn(maxNum)
  fmt.Println("Please input your guess")
  for {
    //读取一行
    var guess int
    _, err := fmt.Scanf("%d\r\n", &guess)
    if err != nil {
      fmt.Println("Invalid input. Please enter an integer value")
      continue
    }
    fmt.Println("You guess is", guess)
    //猜测逻辑
    if guess > secretNumber {
      fmt.Println("Your guess is bigger than the secret number. Please try again")
    } else if guess < secretNumber {
      fmt.Println("Your guess is smaller than the secret number. Please try again")
    } else {
      fmt.Println("Correct, you Legend!")
      break
    }
  }
}

命令行词典

学习内容:

  1. 调用第三方的API去查询翻译
  2. 使用Go发送http请求
  3. 解析Json
  4. 使用代码生成提高开发效率

实现:

翻译网址:fanyi.caiyunapp.com

  1. 打开开发者模式(右键选择检查或者直接Chrome中F12)

  2. 随便翻译(为了抓包)

  3. network → 找到dict请求(每个网站的请求不同,需要找到翻译的那个请求)

  4. 查看Headers、Preview、Payload,提取关键信息

    image_4Vt6B7STCt.png

    image_wFgkGQKieY.png

    image_ZsoguHdDBz.png

  5. 这个请求比较多和复杂,自己实现会麻烦,可以通过下面方式进行一个快速生成请求

    1. 右击network中刚刚找到的那个dict请求,选择Copy → Copy as cURL

    2. 代码生成的网址curlconverter.com/#go ,打开网址选择Go之后直接复制刚刚的cURL,然后复制生成的代码(这网站目前只能够输入cURL(bash) 还不支持cURL(cmd)

    3. 有几个Header比较复杂,所以代码生成的时候是会发生转移错误,在这个例子中直接删掉即可,可以正常运行的(但是我这里并没有报错,可以正常运行)

      代码:

      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")
        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)
      }
      
       仔细看代码是可以看出标准库的强大的,还有一些要点,例如40-44行代码中先判错再defer关闭流
      
       上面代码执行之后可以得到Response的body
      
  6. 创建与Response一一对应的结构体来反序列化Json,Response也比较复杂,所以还是使用代码生成

    1. 网址:oktools.net/json2go
    2. 翻译网站的Response中的内容复制粘贴进网站中可以生成对应的结构体(记住是翻译网站的,上面程序输出的bodyText不行)
  7. 打印之前先把bodyText反序列化,然后打印结构体

      var dictResponse DictResponse
      err = json.Unmarshal(bodyText, &dictResponse)
      if err != nil {
        log.Fatal(err)
      }
      fmt.Printf("%#v\n", dictResponse)
    
  8. 反序列化之前需要在前面进行一个状态码的检验,找出结构体当中我们需要的内容,只打印这部分就行的

      if resp.StatusCode != 200 {
        log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
      }
    
      fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
      for _, item := range dictResponse.Dictionary.Explanations {
        fmt.Println(item)
      }
    
  9. 最后需要先做一个命令行参数的判断,如果参数不等于两个(第一个是启动路径),那么就打印错误并且退出

    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就是需要翻译的词
      query(word)
    }
    
  10. 试验结果

    image_W1Pg6yk6Gq.png

  11. 课后作业,增加另一种翻译引擎的支持,再修改代码实现并行请求两个翻译引擎来提高响应速度

    1. 最后是用了金山词霸的翻译,因为有一些是Preview里面有但是Response里面没有,要么就是Response里面的字符是转码过的

    2. 最终实现的功能是命令行可以同时输入多个需要翻译的单词,将并行交替运用两个引擎进行查询

      91e18aead99dcf37a470c35694d23e7_gZeXXoPd-Z.png

总结

本节课也是参加青训营的第一节正式课,还是收获满满的,虽然自己之前学过go语言,但是老师还是讲了很多自己以前没学到的东西,也是给了自己查漏补缺的方向。实战当中的两个小作业也是挺有意思的,尤其是第二个,之前自己只知道通过一些开发者的API去进行一个调用,这个小作业用的却是http访问去使用网页的功能,不过这个还是有一些局限性,在找其他能用的翻译网站的时候发现了一些网站要么不用json返回、要么就是返回内容是经过加密的,不过还是学到了蛮多有意思的东西。最后就是那几个宝藏网站,一个是通过cURL生产对应代码,另一个就是可以自动生成json结构体,真是太棒了。