「1」Golang 运行 JavaScript|青训营笔记

213 阅读3分钟

创建时间: May 16, 2022 11:30 AM

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

本文是课程『「5.6」Golang 语言基础』的回忆记录,课上的实验让我印象深刻,写作时间是「5.16」正好是课后的第十天。

🤖 Lab2 爬虫-命令行词典

课上:

由讲师从零实现调用彩云翻译 API 完成一个简单的命令行词典。

课后 Lab:

拓展 API,调用其他翻译 API 实现翻译功能。

  • 为了兼容多个 API,设计 Translator 接口和 Result 结构。

    • do(string)
    • Result 用来规定好输出样式

曾经为了获得一个不能编辑的表格数据使用 Golang 爬取过xx表格的数据 ,课上看这部分的操作的时候感觉非常亲切。

// Result 英译中
type Result struct {
  // 音标
  En, Am string
  Means  []string
}
​
func (r Result) String() string {
  var wordMean string
  for _, mean := range r.Means {
    wordMean += mean
  }
  return fmt.Sprintf("英 %s\n美 %s\n\t%v", r.En, r.Am, r.Means)
}
​
type Translator interface {
  Do(word string) Result
}

由于之前使用过百度 API,注册过百度开发者,所以直接选择了百度通用翻译 API。

阅读文档后,很快就理解了如何完成 API 调用。但 API 上线比较久了(用的 POST/URL),编码过程不是特别顺利。

很快,发觉不对劲儿。

因为百度通用 API 翻译只有文本的翻译结果,结果比较单一。

但咱做的是命令行词典,词典没有多种词意以及单词音标是不算合格的。

接着便开始了长达数小时到折腾。

  • 进入网页版本百度翻译,使用 Chrome-NetWork 工具分析网页请求。

    • 提取出来关键字段

      • token
      • sign
      • query
    • 字段分析

      • query 为输入的查询目标
      • token 为客户端鉴权字段
      • 而 sign 则完全未知 932273.711296 看似一个浮点数,我在切换多次 query 后发现这个参数可能与输入 query 绝对相关。除此以外还是一无所知。
    • 寻找问题解决方案

      在网上浏览了非常多帖子后,找到一种可行的解决思路。

      • 首先 sign 和 query 参数相关这点是成立的。
      • 其次,sign 参数顾名思义;签名,是用作服务器端鉴权用的标识。
      • 那么这个 sign 是如何生成以及如何获得的,为了复现签名,我从 JS 代码中将它找了出来,做了一些简单的调试和修改以后,我确信是这段函数。

      不过这中间使用的方式是 Python 运行的 JS 代码实现确认的,随即开始寻找有关 golang 调用 JS 相关的资料。最终找到了 "github.com/robertkrimen/otto"

      由于目录结构比较复杂,没有在这个简单需求中使用文件 io(因为最后打包以后处理 js 文件会很麻烦),直接将 js 字符串作为 consts 存起来。

      • 最终调用代码

        package signer
        ​
        import (
          "github.com/robertkrimen/otto"
          "log"
          "runtime"
        )
        ​
        var (
          p = "/"
        )
        ​
        func init() {
        ​
          if runtime.GOOS == "windows" {
            p = "\"
          }
        }
        ​
        // Sign 返回百度的校验签名
        func Sign(word string) string {
          js := getJS()
          vm := otto.New()
          _, err := vm.Run(js)
          if err != nil {
            log.Fatalln("run err!", err.Error())
        ​
          }
          res, err := vm.Call("e", nil, word)
          if err != nil {
            log.Fatalln("call err!", err.Error())
          }
          return res.String()
        }
        ​
        // 加载 js 脚本
        func getJS() string {
          // pwd, _ := os.Getwd()
          // jsFile := "baiduv2.js"
          //
          // bytes, err := ioutil.ReadFile(pwd + p + jsFile)
          // if err != nil {
          //  log.Fatalln(err)
          // }
          // return string(bytes)
          return jsTOSign
        }
        

      测试:

      === RUN TestBaiDuAPIV2 英 həˈləʊ 美 həˈloʊ

      [int./n.你好, (用于问候、接电话或引起注意)喂, (表示惊讶或认为 别人说了蠢话或没有注意听)嘿; ]

      --- PASS: TestBaiDuAPIV2 (0.91s) PASS