【Go入门】案例1:在线词典

108 阅读4分钟

这里我们采用“彩云小译”这一在线翻译网站(彩云小译 - 在线翻译 (caiyunapp.com))来完成Go语言实战案例的第1个案例——在线词典。 image.png 点击“检查”该页面,Network中“dict”那一条,如果Payload和Preview显示如下说明没有问题: image.png image.png 以及该网页请求含有多个Headers,Headers中请求方式为POST。 image.png 下面复制该请求的cURL image.png 在命令行(cmd)输入,查看返回信息如下。 image.png 并进入网站Convert curl commands to code (curlconverter.com)转换该cURL: image.png 下面进行这部分代码的解读:

package main

// 导包
import (
	"fmt"
	"io"
	"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("Origin", "http://fiddle.jshell.net")
    // req.Header.Set("Accept-Encoding", "gzip, deflate")
    req.Header.Set("Accept-Language", "en-US,en;q=0.8")
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.35")
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
    req.Header.Set("Accept", "*/*")
    req.Header.Set("Referer", "http://fiddle.jshell.net/_display/")
    req.Header.Set("X-Requested-With", "XMLHttpRequest")
    req.Header.Set("Connection", "keep-alive")
    // 发起请求
    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: image.png 但是这里翻译“good”,而且是通过JSON字符串输入的形式。下面将用到JSON序列化来解决生成RequestBody的问题。

package main

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

// 构造结构体(这里像c)
type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}

func main() {
    client := &http.Client{}
    // 先初始化请求(利用结构体声明)
    request := DictRequest{TransType: "en2zh", Source: "good"}
    // 利用Marshal将请求序列化成一个byte数组(请求的序列化)
    buf, err := json.Marshal(request)
    if err != nil {
            log.Fatal(err)
    }
    // 将序列化后的byte数组转化为字符串类型的data
    var data = bytes.NewReader(buf)
    req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
    if err != nil {
            log.Fatal(err)
    }
    req.Header.Set("Connection", "keep-alive")
    req.Header.Set("DNT", "1")
    req.Header.Set("os-version", "")
    req.Header.Set("sec-ch-ua-mobile", "?0")
    req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
    req.Header.Set("app-name", "xy")
    req.Header.Set("Content-Type", "application/json;charset=UTF-8")
    req.Header.Set("Accept", "application/json, text/plain, */*")
    req.Header.Set("device-id", "")
    req.Header.Set("os-type", "web")
    req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
    req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
    req.Header.Set("Sec-Fetch-Site", "cross-site")
    req.Header.Set("Sec-Fetch-Mode", "cors")
    req.Header.Set("Sec-Fetch-Dest", "empty")
    req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
    req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
    req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
    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)
}

可见v2达到的结果是一样的。 image.png 然后进行解析ResponseBody,但是由于单独去看(前文有图)Response的结构是十分复杂的(Preview是Response的Pretty形式),所以还是建议通过工具网站完成。这里我们只需要JSON转Go的功能。 image.png 通过网站JSON转Golang Struct - 在线工具 - OKTools复制Response中的内容, 将之转为Go的代码。(代码量不多的情况下一般选择“转换-嵌套”即可) image.png 具体代码分析如下。

package main

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

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

// 加入的ResponseBody结构体
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"`
}

func main() {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}
	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)
	}
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("DNT", "1")
	req.Header.Set("os-version", "")
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
	req.Header.Set("app-name", "xy")
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("device-id", "")
	req.Header.Set("os-type", "web")
	req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
	req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("Sec-Fetch-Site", "cross-site")
	req.Header.Set("Sec-Fetch-Mode", "cors")
	req.Header.Set("Sec-Fetch-Dest", "empty")
	req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
        // 读取响应体为变量bodyText
	bodyText, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
        // 声明dictResponse为DictResponse结构体类型的变量
	var dictResponse DictResponse
        // Unmarshal对结构体反序列化到bodyText中
        // &表示指定dictResponse结构体
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", dictResponse)
}

运行v3结果如下。 image.png 下面是v4进行改进的地方: 将原来main方法中的代码封装到query方法中,且方法参数输入为请求体结构的一部分。 image.png image.png 而main方法中先判断参数长度是否不为2,然后执行query方法。 image.png v4是最后的效果。 image.png