【golang】2、http client、代理、爬图

在这里插入图片描述

用 golang 可以很方便的爬图(http 下载图片,存储为 jpg 格式)。

一、http client

http client 有如下最佳实践

  • 尽量用 default http client:默认的 http client 设置了很多合适的参数(如超时、连接池),也便于在各请求间复用(如先将获取到的 token 存储到 client 内,后续所有请求都可用此 token)
  • 要设置 timeout:防止因网络 or 下游 server 的问题,而盲等,导致自己阻塞,降低拥堵体验
  • 谨慎处理 redirects:若 server 不存在某资源时可能会用 3xx redirect 到新目的地。然而若 client 未合适处理,可能导致 loop redirect。因此需要如下做法:
    • 校验 redirect url
    • 限制 redirect 次数
  • reuse connections:可用连接池实现,可减少新建连接的开销、降低延迟、增加流量。
  • 默认会打开 Keep-Alive,除非需求是在一个连接上的数据请求密度不高则可手动关闭。详见实验

二、反向代理

正向代理、反向代理 区别

2.1 http

package main

import (
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
)

// NewProxy 拿到 remoteURL 后,创建一个反向代理
func NewProxy(remoteURL string) (*httputil.ReverseProxy, error) {
	remote, err := url.Parse(remoteURL)
	if err != nil {
		return nil, err
	}

	proxy := httputil.NewSingleHostReverseProxy(remote)
	proxy.Director = func(req *http.Request) {
		//req.Header = ctx.Request.Header
		req.Host = remote.Host
		req.URL.Scheme = remote.Scheme
		req.URL.Host = remote.Host
		req.URL.Path = "/metrics"
	}

	return proxy, nil
}

// ProxyRequestHandler 使用 proxy 处理请求
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) { proxy.ServeHTTP(w, r) }
}

func main() {
	proxy, err := NewProxy("http://127.0.0.1:7000/metrics") // 初始化反向代理并传入真正后端服务的地址
	if err != nil {
		panic(err)
	}
	http.HandleFunc("/api/proxy/metrics", ProxyRequestHandler(proxy)) // 使用 proxy 处理所有请求到你的服务
	log.Fatal(http.ListenAndServe(":8080", nil))
}

2.2 gin

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
	"net/http/httputil"
	"net/url"
)

// NewProxy 拿到 remoteURL 后,创建一个反向代理
func NewProxy(remoteURL string) (*httputil.ReverseProxy, error) {
	remote, err := url.Parse(remoteURL)
	if err != nil {
		return nil, err
	}

	proxy := httputil.NewSingleHostReverseProxy(remote)
	proxy.Director = func(req *http.Request) {
		//req.Header = ctx.Request.Header
		req.Host = remote.Host
		req.URL.Scheme = remote.Scheme
		req.URL.Host = remote.Host
		req.URL.Path = "/metrics"
	}

	return proxy, nil
}

func proxy(c *gin.Context) {
	proxy, err := NewProxy("http://127.0.0.1:7000/metrics") // 初始化反向代理并传入真正后端服务的地址
	if err != nil {
		panic(err)
	}
	proxy.ServeHTTP(c.Writer, c.Request)
}

func main() {
	r := gin.Default()
	r.GET("/api/proxy/metrics", proxy)
	r.Run(":8080")
}

三、爬取并存储 jpg

package clients

import (
	"fmt"
	"github.com/sirupsen/logrus"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

var httpCli = &http.Client{
	Timeout: 10 * time.Second,
}

type spiderCli struct {
	httpCli *http.Client
	txtFile *os.File
}

func Spider() *spiderCli {
	txtFile, err := os.OpenFile(configs.TxtFileName(), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		logrus.Fatal(err)
	}
	return &spiderCli{
		httpCli: httpCli,
		txtFile: txtFile,
	}
}

func (c *spiderCli) Download(url string) ([]byte, error) {
	res, err := c.httpCli.Get(url)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	if res.StatusCode == http.StatusNotFound {
		return nil, fmt.Errorf("got invalid status code: %v", res.StatusCode)
	}
	return io.ReadAll(res.Body)
}

// Save img.jpg and txt
func (c *spiderCli) Save(fid int64, bytes []byte, plateText, plateColor, plateType string) error {
	// img
	fImgName := fmt.Sprintf("%v.jpg", fid)
	fImg, err := os.Create(fImgName)
	if err != nil {
		return err
	}
	defer fImg.Close()
	writtenImg, err := fImg.Write(bytes)
	if err != nil {
		return err
	}
	if writtenImg == 0 {
		os.Remove(fImgName)
	}
	// txt
	line := strings.Join([]string{fImgName, plateText, plateColor, plateType}, ",")
	writtenTxt, err := c.txtFile.WriteString(line + "\n")
	_ = writtenTxt
	return err
}

func (c *spiderCli) Close() {
	c.txtFile.Close()
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值