大家好,我是杨小爽,上一篇讲了go语言也可以写爬虫,介绍了go语言的基础语法。
上一篇文章:
爬虫基础|爬虫语言的新选择?
今天我们来学习go语言中的网络请求库,官方标准库net/http,net/http提供了HTTP客户端和服务端的实现,类似python中的urllib3。
下面我们以例子来学习。
1、发送请求
先要导入net/http:
import (
"net/http"
)
然后,使用Get请求来获取某个网页:
// 获取go语言中文网标准库页面源码
resp, err := http.Get("https://studygolang.com/pkgdoc")
请求后返回的resp是一个Response结构体指针地址,err是一个错误。go中很明确的一点是几乎每执行一个操作都会返回错误信息。
除了Get请求外,还有其他请求方式:
// 语法1:func Post(url string, bodyType string, body io.Reader) (resp *Response, err error)
resp, err := http.Post("https://example.com", "image/jpeg", &buf)
// 语法2:func PostForm(url string, data url.Values) (resp *Response, err error)
resp, err := http.PostForm("https://example.com",url.Values{"key": {"Value"}, "id": {"123"}})
语法1中bodyType为Post数据的类型,body是post的数据,比如上传一个文件或发送一个图片都可以采用这种方式。
语法2中data会被编码为请求的主体,然后post请求给知道url,比如用于上传表单等。
2、响应内容
我们可以读取服务器响应的内容:
resp, err := http.Get("https://example.com")
if err != nil {
panic(err)
}
defer resp.Body.Close() // 读取内容后要关闭响应的主体
body, err:= ioutil.ReadAll(resp.Body) // 读取内容
if err != nil {
panic(err)
}
fmt.Printf("%s", body) // 以字符串形式打印
从Response中读取出的响应是字节切片,如果需要以字符串进行解析,可以使用string()来显式转换,然后再解析。
python的requests包会自动解码来自服务器的内容,而go的net/http并不行,需要我们自己根据实际编码进行转换,这其实也是设计原则的影响,可以稍微吐槽下。
3、自定义请求头
在net/http中添加请求头,需要先建立一个client:
client := &http.Client{} // 建立client
req, err := http.NewRequest("GET", "https://example.com", nil)
if err != nil {
panic(err)
}
// 添加自定义请求头
req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36")
resp, err := client.Do(req)
4、响应状态码
发起请求得到响应后,很多时候需要查看响应状态码,来判断请求情况:
resp, err := http.Get("https://www.example.com")
if err != nil {
panic(err)
}
fmt.Printf("%s\n", resp.StatusCode) // 打印状态码,如200
fmt.Printf("%s\n", resp.Status) //打印状态码跟原因短语,如200 ok
当然,net/http也内置了状态码查询对象:
resp, err := http.Get("https://www.example.com")
if err != nil {
panic(err)
}
// 具体的状态码对象可以查询文档,里面会有详细介绍
if resp.StatusCode==http.StatusOk {
fmt.Printf("%s\n", resp.StatusCode)
}
5、响应头
除了可以添加请求头外,我们还可以获取响应头:
resp, err := http.Get("https://www.example.com")
if err != nil {
panic(err)
}
header := resp.Header //获取得到的是Header结构体
fmt.Printf("%s", header)
// map[Connection:[keep-alive] Content-Type:[text/html; charset=UTF-8] Date:[Sat, 01 Jun 2019 02:22:41 GMT] Etag:["d3ae175b24ac305beeccb7aa5bf4bb773d501f9a"] Server:[openresty] Set-Cookie:[_xsrf=2|4ed4f6b5|6823e2054a31f6477e7a8cb2f1ec81d8|1559355761; Path=/] Vary:[User-Agent, Accept-Encoding]]
Header结构体是一个map:map[string]string。
6、Cookie
如果返回的响应头部带有”Set-Cookie“,便可以去获取它:
resp, err := http.Get("https://www.example.com")
if err != nil {
panic(err)
}
cookies := resp.Cookies() // 获取得到的是[]*Cookie
fmt.Printf("%s", cookies)
// [_xsrf=2|752b7518|68acb15c5ac7128b7c31b1107bd64837|1559356249; Path=/]
Cookie是一个结构体,下面是它的定义方式:
type Cookie struct {
Name string
Value string
Path string
Domain string
Expires time.Time
RawExpires string
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string // 未解析的“属性-值”对的原始文本
}
如果要了解更加详细的获取cookie的方式,请查看文档。
7、超时
你可以设置请求的停止响应时间,即设置超时时间:
// 设置超时时间20秒
var timeout time.Duration
timeout = 20
client := http.Client{Timeout: timeout} // Timeout为零值表示不设置超时
注意一点,该超时限制包括连接时间、重定向和读取回复主体的时间。
8、代理
go中有两种设置代理的方式。
第一种,利用http.Transport:
urli := url.URL()
urlProxy, _ := urli.Parse("http://127.0.0.1:5555")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(urlProxy),
}
}
resp, err := client.Get("https://www.example.com")
第二种,通过环境变量来设置代理:
os.Setenv("HTTP_PROXY", "http://127.0.0.1:5555")
os.Setenv("HTTPS_PROXY", "https://127.0.0.1:5555")
client := &http.Client{}
resp, err := client.Get("https://www.example.com")
9、结尾语
以上介绍的8点便是爬虫中会经常遇到的问题,如果有个性化的需求,建议自己翻阅文档,Go语言中文网的标准库翻译就还不错,下面贴地址,感兴趣的可以看看。
// Go语言中文网
https://studygolang.com/pkgdoc
最后,谢谢大家观看,如果有建议或疑问,请在微信公众号后台发消息联系我。
欢迎关注