最新代码请关注:

https://www.birdy02.com/docs/birdy02-com-request

简介

  1. 一款基于 net/http 二次封装的客户端请求库,支持返回更丰富的响应结构和内容;

  2. 包含多种方法: 目前支持[HEAD/GET/POST]

使用

示例代码不具有时效性,最新代码请查看GitHub: https://github.com/birdy02-com/request

go get github.com/birdy02-com/request

优点

  • POST一键获取请求体;对比:net/http无法直接获取请求body

  • 根据响应内容多层判断获取编码格式

  • 默认返回目标站点的基本信息:Title、Description、Keyword、Favicon路径

  • 增加请求响应时长

  • 丰富默认请求头,使请求看起来像真人一样

主要功能

  1. HEAD 请求方法

  2. GET 请求方法

  3. POST 请求方法

  4. Result 格式化响应内容

  5. IsIpv4 检查一个IP字符串是否为IPv4地址

  6. GetRandomIP 生成一个随机的IP地址

  7. IsPrivateIP 判断IP是否为私有地址

  8. ParseUrl 格式化URL

  9. GetHeader 获取默认请求头

  10. GetSiteBasic 提取网站的基本信息

  11. GetFaviconPath 获取favicon.ico的路径

响应返回格式

// Response 请求的返回结构
type Response struct {
	Basic struct {
		Title       string // 网页标题
		Description string // 网页描述
		Keywords    string // 网页关键字
		Favicon     string // 网页图标路径
	}
	Redirect   string                 // 重定向地址
	Url        string                 // 响应url
	StatusCode int                    // 响应状态码
	Status     string                 // 响应状态 200 ok
	Timer      float64                // 响应时长
	Headers    http.Header            // 响应头
	Body       string                 // 响应体(str)
	Charset    string                 // 检测到的编码方式
	Content    []byte                 // 响应体(byte)
	Json       map[string]interface{} // 响应的Json内容
	Length     int                    // 响应字节
	Proto      string                 // 响应协议
	ProtoMajor int                    // 响应版本号-主
	ProtoMinor int                    // 响应版本号-子
	Request    struct {
		URL     string      // 请求url
		Method  string      //请求方法
		Headers http.Header //请求头
		Body    []byte      // 请求体
	}
}
  • 其实封装自己的请求结构也还是有好处的,可以适配自己的使用习惯,直接默认就自定义好了请求头来模拟真实用户请求,but 实现的代码也是用了一百多行😭

请求使用演示

  • 请求参数结构

// GetRequest 请求的参数结构
type GetRequest struct {
	Timeout        int               // 超时时长
	AllowRedirects bool              // 是否跟随跳转
	Verify         bool              // ssl验证证书忽略
	Headers        map[string]string // 请求头
	Params         map[string]string // 请求参数
	Stream         bool
	Engine         bool
	Data           string              // post请求体
	DataJson       map[string]string   // json格式传入post请求体,会格式化成 xx=xx
	Json           map[string]any      // post请求
	File           map[string][]string // 上传的文件,格式参考 file:['文件名','内容','文件类型']
}
  • 简单请求

if r, e := request.GET(uri); e == nil {
  fmt.Println(res.Basic.Title)
}
  • 自定义参数请求

res, err := request.GET(uri, request.GetRequest{Timeout: 16})
if err != nil {
  return nil, err
}
  • POST请求参数

// 参数类型参考‘1. 请求参数示例’
res, err = request.POST(uri, request.GetRequest{Headers: Header, Data: r.Data, DataJson: r.DataJson, Json: r.Json, File: r.File})
if err == nil{
  fmt.Println(res.Basic.Title)
}

其他

初始化请求参数

// GetRequestGetArg 获取请求参数
func GetRequestGetArg(baseurl string, args GetRequest) (*GetRequest, http.Client, string) {
	baseurl = strings.TrimSpace(baseurl)
	reqArg := GetRequestInit() // 获取请求配置
	// 处理传入参数
	// 超时时长
	if args.Timeout != 0 && args.Timeout != reqArg.Timeout {
		reqArg.Timeout = args.Timeout
	}
	// 是否跟随跳转
	if args.AllowRedirects != reqArg.AllowRedirects {
		reqArg.AllowRedirects = args.AllowRedirects
	}
	// 是否忽略证书
	if args.Verify != reqArg.Verify {
		reqArg.Verify = args.Verify
	}
	// 是否添加Headers
	if args.Headers != nil {
		reqArg.Headers = args.Headers
	}
	// 是否有Params参数
	if args.Params != nil {
		reqArg.Params = args.Params
	}
	// 是否流式传输
	if args.Stream != reqArg.Stream {
		reqArg.Stream = args.Stream
	}

	//proxyURL, err := url.Parse("http://127.0.0.1:8080")
	//if err != nil { }

	// 请求参数设置
	// 创建一个自定义的Transport,并禁用证书验证
	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		//Proxy:           http.ProxyURL(proxyURL),
	}
	// 设置Params
	params := url.Values{}
	for k, v := range reqArg.Params {
		params.Set(k, v)
	}
	uParse, _ := url.Parse(baseurl)
	Params := params.Encode()
	tmpParams := strings.TrimPrefix(strings.TrimPrefix(uParse.RequestURI(), uParse.Path), "?")
	if tmpParams != "" {
		if Params != "" {
			Params = fmt.Sprintf("%s&%s", Params, tmpParams)
		} else {
			Params = tmpParams
		}
	}
	fullURL := fmt.Sprintf("%s://%s%s?%s", uParse.Scheme, uParse.Host, uParse.Path, Params)
	suffixToRemove := "?"
	if strings.HasSuffix(fullURL, suffixToRemove) {
		fullURL = strings.TrimSuffix(fullURL, suffixToRemove)
	}
	var client http.Client
	// 重定向策略
	if !reqArg.AllowRedirects {
		client = http.Client{
			Timeout: time.Duration(reqArg.Timeout) * time.Second, // 转换为time.Duration
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				return http.ErrUseLastResponse
			},
			Transport: transport,
		}
	} else {
		client = http.Client{ // 创建http.Client并配置Timeout
			Timeout:   time.Duration(reqArg.Timeout) * time.Second, // 转换为time.Duration
			Transport: transport,
		}
	}
	return reqArg, client, fullURL
}
  • 在安全工作中,为了请求方便,默认禁止了ssl证书可信度报错

默认请求头

// GetHeader 获取请求头
func GetHeader(args *GetHeaderArgs) http.Header {
	// 预设的User-Agent列表
	userAgents := []string{
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 OPR/112.0.0.0",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.289 Safari/537.36 ",
		"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Core/1.94.202.400 QQBrowser/11.9.5355.400",
		"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
		"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
	}
	randIp := GetRandomIP()
	// 初始化HTTP头
	header := http.Header{}
	rand.Seed(time.Now().UnixNano())
	if args.Engine {
		header.Set("User-Agent", "Baiduspider+(+https://www.baidu.com/search/spider.htm);google|baiduspider|baidu|spider|sogou|bing|yahoo|soso|sosospider|360spider|youdao|jikeSpider;)")
	} else {
		header.Set("User-Agent", userAgents[rand.Intn(len(userAgents))])
	}
	header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
	header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	header.Set("Cache-Control", "no-cache")
	header.Set("Pragma", "no-cache")
	header.Set("CLIENT-IP", randIp)
	header.Set("X-Remote-Ip", randIp)
	header.Set("X-Remote-Addr", randIp)
	header.Set("X-Originating-Ip", randIp)
	header.Set("X-FORWARDED-FOR", randIp)
	header.Set("Connection", "keep-alive")
	header.Set("Upgrade-Insecure-Requests", "1")
	// 根据参数更新HTTP头
	if args.Switch == "Bing" {
		header.Set("Host", "cn.bing.com")
		header.Set("Referer", "https://www.bing.com")
	}
	if args.api != "" {
		if parse, err := url.Parse(args.api); err == nil {
			header.Set("Host", parse.Host) // 提取Host和Referer
		}
		header.Set("Referer", args.api)
	}
	// 传入的header参数
	for k, v := range args.header {
		header.Set(k, v)
	}
	return header
}
  • 默认配置好了请求头,支持每次自定义请求头【替换默认】,使每次请求看起来都像真人

持续更新中...