go基礎語法,一直沒怎么練習,語法這東西不用就忘,工作中多多少少還接觸到爬蟲,索性強制用go來寫,寫爬蟲不是目的,目的是為了練習go語法,強化記憶。
畢竟go處理一些字符串和Python
比,我認為還是十分繁瑣的,而且爬蟲瓶頸是限於網絡和目標地址響應,所以在寫爬蟲性能上,go不一定勝過Python。
已有的輪子
目前已經有了很多他人封裝好的請求庫,但是還是要懂原生庫的用法,以下是我在github上找的封裝請求庫star數量1k+的(以下排名不分先后):
注意以下示例皆要以編寫測試程序的方式書寫,文件名以_test.go
后綴結尾。
GET請求
package http_demo
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"testing"
)
func Requests(url string) (content string, err error) {
// 創建 http 客戶端
client := &http.Client{}
// 創建請求
req, _ := http.NewRequest("GET", url, nil)
// GET 請求攜帶查詢參數
q := req.URL.Query()
q.Add("auth_key", "sky_kk")
q.Add("another_thing", "foo & bar")
req.URL.RawQuery = q.Encode()
//req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
// 設置請求頭
req.Header.Set("User-Agent", "test")
// 發送請求
resp, err := client.Do(req)
if err != nil {
// 格式化返回錯誤
return "", errors.New(fmt.Sprintf("請求出錯 %s", err))
}
// 最后關閉連接
defer resp.Body.Close()
// 讀取內容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", errors.New(fmt.Sprintf("解析內容出錯 %s", err))
}
//fmt.Println(string(body))
return string(body), nil
}
func TestReqUrl(t *testing.T) {
url := "https://httpbin.org/get"
content, err := Requests(url)
if err != nil {
fmt.Println("請求錯誤", err)
return
}
t.Log(fmt.Sprintf("結果 \n %s", content))
}
POST 請求
POST 請求攜帶參數的方式,根據Content-Type
不同 有多種請求方式
POST from請求
package http_demo
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"testing"
)
func PostReq(targetUrl string) (content string, err error) {
// 最簡單的發送 post請求 攜帶 form參數
resp, err := http.PostForm(targetUrl, url.Values{"username": {"wxy"}, "password": {"wxy666"}})
// 相當於這種
//req.Header.Set("Content-Type", "multipart/form-data")
if err != nil {
return "", errors.New(fmt.Sprintf("請求錯誤 %s", err))
}
fmt.Println(fmt.Sprintf("響應狀態 %s", resp.Status))
// 記得關閉客戶端
defer resp.Body.Close()
// 讀取內容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", errors.New(fmt.Sprintf("讀取內容錯誤 %s", err))
}
content = string(body)
return content, nil
}
func TestReq3(t *testing.T) {
targetUrl := "https://httpbin.org/post"
content, err := PostReq(targetUrl)
if err != nil {
fmt.Println("請求錯誤", err)
return
}
fmt.Println(content)
}
POST JSON 方式請求
package http_demo
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"testing"
)
// post form請求 務必設置 Content-Type
func ReqPostForm(targetUrl string) (content string, err error) {
// 創建 http 客戶端
client := &http.Client{}
form := url.Values{}
form.Add("ln", "ln222")
form.Add("ip", "1.1.1.1")
form.Add("ua", "ua123")
// 創建請求
req, _ := http.NewRequest("POST", targetUrl, strings.NewReader(form.Encode()))
req.Header.Set("User-Agent", "test")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
// 發送請求
resp, err := client.Do(req)
if err != nil {
// 格式化返回錯誤
return "", errors.New(fmt.Sprintf("請求出錯 %s", err))
}
// 最后關閉連接
defer resp.Body.Close()
// 讀取內容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", errors.New(fmt.Sprintf("解析內容出錯 %s", err))
}
return string(body), nil
}
// post json方式請求
func ReqPostJson(targetUrl string) (content string, err error) {
var jsonStr = []byte(`{"title":"this is a title", "cate": 1}`)
req, _ := http.NewRequest("POST", targetUrl, bytes.NewBuffer(jsonStr))
req.Header.Set("X-Custom-Header", "myvalue")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", errors.New(fmt.Sprintf("請求出錯 %s", err))
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body), nil
}
func TestReqPost(t *testing.T) {
targetUrl := "https://httpbin.org/post"
c1, _ := ReqPostForm(targetUrl)
t.Log(fmt.Sprintf("\nfrom表單返回: \n %s", c1))
c2, _ := ReqPostJson(targetUrl)
t.Log(fmt.Sprintf("\nJson請求返回: \n %s", c2))
}