Golang语言实战案例之在线词典 | 青训营

42 阅读6分钟

在线词典

内容

通过使用第三方API获取数据,达到读取用户输入的数据并返回其音标、汉译的目的。

获取翻译步骤:

  1. 打开彩云翻译官网
  2. 输入需要查询的单词
  3. 点击翻译按钮
  4. 等待页面返回结果

使用第三方API获取数据步骤:

  1. 首先,需要打开浏览器的开发者工具。可以通过F12或右键网页页面后点击检查选项来打开它。

  2. 在开发者工具中,找到网络标签页或者Network并打开它。

  3. 应当在原文文本框输入一个已知存在的单词,并在点击翻译后注意观察网络标签页下记录的网络请求。

  4. 通过筛选网络请求记录,很容易找到dict请求。其中,Payload下的Preview便是需要注意的内容。

  5. 右击dict选择Copy中的Copy as cURL。

  6. 并把复制的内容粘贴到解析网站解析。

    解析内容大致为:

    package main
    
    import (
    	"fmt"
    	"io"
    	"log"
    	"net/http"
    	"strings"
    )
    
    func main() {
    	client := &http.Client{}
    	var data = strings.NewReader(`{"trans_type":"en2zh","source":"Strong"}`)
    	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    	if err != nil {
    		log.Fatal(err)
    	}
    	req.Header.Set("authority", "api.interpreter.caiyunai.com")
    	req.Header.Set("accept", "application/json, text/plain, */*")
    	req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
    	req.Header.Set("app-name", "xy")
    	req.Header.Set("cache-control", "no-cache")
    	req.Header.Set("content-type", "application/json;charset=UTF-8")
    	req.Header.Set("device-id", "35d05b0ae7523f22cde64f11f9204282")
    	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
    	req.Header.Set("os-type", "web")
    	req.Header.Set("os-version", "")
    	req.Header.Set("pragma", "no-cache")
    	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
    	req.Header.Set("sec-ch-ua", `"Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"`)
    	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", "cross-site")
    	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
    	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
    	resp, err := client.Do(req)
    	if err != nil {
    		log.Fatal(err)
    	}
    	defer resp.Body.Close()
    	bodyText, err := io.ReadAll(resp.Body)
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Printf("%s\n", bodyText)
    }
    
    1. 第12行我们创建了一个 HTTP Client,创建的时候可以指定很多参数,包括请求的超时时间,是否使用Cookie 等。
    2. 在第14行,我们构造了一个 HTTP 请求,这是一个POST请求,然后会用到http.NewRequest,第一个参数是 HTTP请求方法POST, 第二个参数是URL, 最后一个参数是RequestBody。由于RequestBody可能很大,为了支持流式发送,它是一个只读流。我们用了strings.NewReader来把字符串转换成一个流,这样我们就成功构造了一个 HTTP Request。
    3. 18~35行,我们对这个 HTTP Request设置了一些Request Header。 接下来我们把我们调用client.Do,就能得到Response。
  7. 复制需要的解析内容。

生成根据参数变化的RequestBody

此时我们需要使用到json序列化和反序列化软件包,并编写对应的结构体。

type DictRequest struct {
	TransType string `json:"trans_type"` // 请求类型
	Source    string `json:"source"`     // 要查询的单词
	UserID    string `json:"user_id"`    // 用户ID
}

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)
}

解析ResponseBody

在这里插入图片描述

  1. 复制上述图片框起的数据
  2. 借助工具:OKToolsJSON转Go功能,可以很便捷的拿到反序列化所需的结构体
  3. 使用转换-嵌套
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"` 
}

剩下的步骤:

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)
}


nc 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) 
  1. 发送HTTP请求并获取响应
  2. 读取响应体内容
  3. 检查响应状态码
  4. 创建一个词典响应对象
  5. 解析JSON响应数据到对象中
  6. 打印单词的英式和美式发音
  7. 遍历解释列表并打印
  8. 关闭响应体
  9. 检查命令行参数数量
  10. 获取要查询的单词
  11. 调用查询函数进行查询

运行程序:

go run main.go Strong

输出

在这里插入图片描述

完整代码

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"`    // 用户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"` // ID
		Item struct {
			Source string `json:"source"` // 来源
			Target string `json:"target"` // 目标
		} `json:"item"` // 项目信息
		ImageURL  string `json:"image_url"`  // 图片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{} // 创建一个HTTP客户端
	request := DictRequest{TransType: "en2zh", Source: word} // 创建一个词典请求对象,指定请求类型和要查询的单词
	buf, err := json.Marshal(request) // 将请求对象转换为JSON格式的字节流
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf) // 创建一个字节流读取器,用于读取JSON数据
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data) // 创建一个POST请求,指定URL和请求体
	if err != nil {
		log.Fatal(err)
	}
	req.Header.Set("authority", "api.interpreter.caiyunai.com")
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
	req.Header.Set("app-name", "xy")
	req.Header.Set("cache-control", "no-cache")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "35d05b0ae7523f22cde64f11f9204282")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("pragma", "no-cache")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"`)
	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", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")

  resp, err := client.Do(req) // 发送HTTP请求并获取响应
  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) // 解析JSON响应数据到对象中
  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) // 调用查询函数进行查询
}