Go语言实战——在线词典

本文将通过一个简单的在线词典案例体会如何利用 Go 语言来发送 http 请求并解析响应

1 在线翻译 API 获取

打开彩云翻译网站 https://fanyi.caiyunapp.com/

打开浏览器控制台,输入要翻译的单词,点击翻译,可以看到浏览器发送的请求

鼠标右键相应的请求,选择复制 cURL

将粘贴的命令复制到命令行中执行,会返回一大串 json 字符串,接下来我们利用 Golang 来发送请求

2 利用 Golang 发送 http 请求

这里我们利用网站来生成 Golang 发送 http 请求的代码 https://curlconverter.com/go/

打开网站,将浏览器中复制的 CURL 命令粘贴进去,得到代码

将代码复制到 IDE 中查看,运行代码,会输出一串 json 字符串

我们来看一下生成的代码:

  • 首先创建了一个HTTP client,创建的时候可以指定很多参数,包括请求的超时时间、是否使用cookie等。
  • 接下来是构造一个HTTP请求,这是一 post 请求,会用到 HTTP.NewRequest。第一个参数是 http 方法,第二个参数是URL,最后一个参数是body,body可能很大,为了支持流式发送,是一个只读流。用 strings.NewReader 来把字符串转换成一个流,这样我们就成功构造了一个HTTP request
  • 接下来需要对这个 HTTP request 设置 header ,调用 client.do 就能得到 response 如果请求失败的话,error 会返回非 nil,会打印错误并且退出进程。
  • response中包括 HTTP状态码,response header 和 body.。body同样是一个流,在 golang 里面,为了避免资源泄露,需要加一个 defer 来手动关闭这个流,这个defer会在这个函数运行结束之后去执行。
  • 接下来用 io.ReadAll 来读取这个流,就能得到整个 body,再用 print 打印出来。
package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
)

func main() {
    // 创建一个 http client
	client := &http.Client{}
    // 发送 post 请求,携带data参数
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"content"}`)
    // 创建一个 post 请求
	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("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "975c9b3a88bdb5d862f1a195b157e53e")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"`)
	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/115.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)
}

3 利用变量输入请求参数

现在我们已经可以发送请求并且打印 json,但是请求参数是固定的,我们希望可以通过变量来输入,此时就需要创建一个结构体并序列化成为 json 字符串

请求参数结构如下:

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

修改开头的代码:

client := &http.Client{}
	//var data = strings.NewReader(`{"trans_type":"en2zh","source":"content"}`)
	request := DictRequest{TransType: "en2zh", Source: word}
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)

运行代码,如果正确输出 json 字符串则说明程序正确,接下来我们用同样的方法处理响应

4 处理响应

  • 代码写到这里,我们得到的响应还只是一段 json 字符串,不方便阅读,需要通过反序列化的手段将字符串保存到结构体中
  • 在 python 和 JavaScript 等弱类型语言中,json 字符串直接可以转化成字典或者对象操作,但是 Golang 是强类型语言,必须先创建一个结构体来接收结果,然后再对该结构体进行操作
  • 由于响应的 json 字符串十分复杂,我们可以通过在线工具生成其对应的结构体,在线转换网址 https://oktools.net/json2go

然后我们将生成的结构体复制到代码中,并在末尾加入处理响应的逻辑

type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []interface{} `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 := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	//fmt.Printf("%s\n", bodyText)
	// 将得到的 json 字符串解析到结构体
	var dicResponse DictResponse
	err = json.Unmarshal(bodyText, &dicResponse)
	if err != nil {
		log.Fatal(err)
	}

最后,我们只需要结构体中的部分字段,包括音标和单词翻译,因此我们选择性输入

fmt.Println(word, "UK:", dicResponse.Dictionary.Prons.En, "US:", dicResponse.Dictionary.Prons.EnUs)
	for _, item := range dicResponse.Dictionary.Explanations {
		fmt.Println(item)
	}

5 优化

代码写道这里,核心功能都已经完成了,接下来将代码做一些优化

  1. 将我们的业务代码封装成函数
func query(word string) {
	client := &http.Client{}
	//var data = strings.NewReader(`{"trans_type":"en2zh","source":"content"}`)
	request := DictRequest{TransType: "en2zh", Source: word}
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)

	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("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "975c9b3a88bdb5d862f1a195b157e53e")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"`)
	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/115.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)

	var dicResponse DictResponse
	err = json.Unmarshal(bodyText, &dicResponse)
	if err != nil {
		log.Fatal(err)
	}

	//fmt.Printf("%#v\n", dicResponse)

	fmt.Println(word, "UK:", dicResponse.Dictionary.Prons.En, "US:", dicResponse.Dictionary.Prons.EnUs)
	for _, item := range dicResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
}
  1. 编写 main 函数
func main() {
	var word string
	fmt.Println("请输入要查询的单词:")
	fmt.Scanln(&word)
	query(word)
}
  1. 运行结果示例

6 完整代码

package main

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

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 {
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []interface{} `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{}
	//var data = strings.NewReader(`{"trans_type":"en2zh","source":"content"}`)
	request := DictRequest{TransType: "en2zh", Source: word}
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)

	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("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "975c9b3a88bdb5d862f1a195b157e53e")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"`)
	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/115.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)

	var dicResponse DictResponse
	err = json.Unmarshal(bodyText, &dicResponse)
	if err != nil {
		log.Fatal(err)
	}

	//fmt.Printf("%#v\n", dicResponse)

	fmt.Println(word, "UK:", dicResponse.Dictionary.Prons.En, "US:", dicResponse.Dictionary.Prons.EnUs)
	for _, item := range dicResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
}

func main() {
	var word string
	fmt.Println("请输入要查询的单词:")
	fmt.Scanln(&word)
	query(word)
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值