GRequests: 讓 HTTP 服務人類
熟悉我的讀者朋友們都知道,我早期是寫 Python 的,現在主力語言是 Go。開始接觸 Go 語言以後,我發現 Go 自帶的 net/http
請求庫不夠好用,好在我沒用 Go 寫過一行爬蟲代碼,平時 net/http
庫用的也就比較少,不是每天都用,也就忍了。
最近我在網上衝浪時,無意間發現了 grequests
這個庫,靈感來源於 Python 生態中大名鼎鼎的 requests
庫。顧名思義,這個庫就是用來發送 HTTP 請求的。grequests
的出現讓我眼前一亮,我的 Go 工具包又增添了一員,本文就來帶大家一起體驗下 grequests
庫的強大之處。
簡介
以前寫 Python 的時候,要說用起來最舒服的 HTTP 請求庫,當屬 requests
庫了,沒有之一。requests
庫官方文檔標題是 Requests: HTTP for Humans™
,有人將其翻譯爲讓 HTTP 服務人類
,這也是本文標題的由來,可見這個庫旨在降低我們在發送 HTTP 請求時寫代碼的心智負擔。
根據我的實際使用體驗,這個標題絕對不是在吹牛,requests
庫的 API 設計是真的符合直覺,也符合 Python 哲學。
grequests
即 Go 版本的 requests
,官方介紹其爲:A Go "clone" of the great and famous Requests library
,看來 grequests
庫是 requests
庫的復刻。
使用 net/http
發送 HTTP 請求
在具體介紹 grequests
庫的使用方法之前,我們先來回顧下 net/http
發送 HTTP 請求的用法,以此來對比 grequests
庫的強大。
使用 net/http
發送 HTTP GET
請求示例代碼如下:
// 發起 HTTP GET 請求
resp, _ := http.Get("https://httpbin.org/get")
defer resp.Body.Close() // 確保關閉響應體
// 讀取響應體內容
body, _ := io.ReadAll(resp.Body)
// 將響應體打印爲字符串
fmt.Println(string(body))
NOTE: 爲了保持代碼邏輯清晰,這裏只展示了代碼主邏輯,並且代碼中忽略了所有錯誤處理。後文中所有示例代碼都會如此,完整代碼可以在文末給出的示例代碼 GitHub 鏈接中獲取。
NOTE: 示例中請求的網址
httpbin.org
是一個叫httpbin
的開源項目,它提供了一個可用於測試和調試 HTTP 客戶端(如瀏覽器、命令行工具、自定義腳本或任何發送 HTTP 請求的軟件)的 HTTP 服務。這個服務模擬了各種 HTTP 場景,可以幫助開發者測試自己的 HTTP 客戶端是否正確處理各種 HTTP 方法、響應等。httpbin
也是requests
作者Kenneth Reitz
開發並維護的項目。
使用 net/http
發送 HTTP POST
請求示例代碼如下:
// 創建一個要發送的數據結構並編碼爲 JSON
data := map[string]any{
"username": "user",
"password": "pass",
}
jsonData, _ := json.Marshal(data)
// 創建 POST 請求
url := "https://httpbin.org/post"
request, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonData))
// 添加請求頭,指明發送的內容類型爲 JSON
request.Header.Set("Content-Type", "application/json")
// 發送請求並獲取響應
client := &http.Client{}
resp, _ := client.Do(request)
defer resp.Body.Close()
// 讀取響應體內容
body, _ := io.ReadAll(resp.Body)
// 將響應體打印爲字符串
fmt.Println(string(body))
可見,使用 net/http
發送 HTTP GET
請求的代碼還算友好,但是發送 POST
請求真的是過於繁瑣。
好在,現在我們有了 grequests
。
GRequests
要使用 grequests
庫就要先對其進行安裝:
$ go get -u github.com/levigross/grequests
極速開始
使用 grequests
發送 HTTP GET
請求示例代碼如下:
resp, _ := grequests.Get("https://httpbin.org/get", nil)
defer resp.Close() // 確保關閉響應體
fmt.Println(resp)
fmt.Println(resp.String()) // 實現了 `fmt.Stringer` 接口
沒錯,就是這麼簡單,僅需三行代碼即可打印響應結果。一切都是那麼的自然,符合直覺,這就是 requests
API 的強大之處。
我們不再需要使用 io.ReadAll
去讀取 HTTP 響應體內容,*grequests.Response
提供了 String
方法,即實現了 fmt.Stringer
接口。
QueryString
grequests
庫發送 GET
請求時傳遞 QueryString 參數方式如下:
ro := &grequests.RequestOptions{
Params: map[string]string{"Hello": "Goodbye"},
}
resp, _ := grequests.Get("https://httpbin.org/get?Hello=World", ro)
defer resp.Close()
fmt.Println(resp)
fmt.Println(resp.RawResponse.Request.URL)
執行以上示例代碼,打印結果如下:
{
"args": {
"Hello": "Goodbye"
},
"headers": {
"Accept-Encoding": "gzip",
"Host": "httpbin.org",
"User-Agent": "GRequests/0.10",
"X-Amzn-Trace-Id": "Root=1-6657cca7-715b811a08dff4420f487570"
},
"origin": "69.28.52.250",
"url": "https://httpbin.org/get?Hello=Goodbye"
}
https://httpbin.org/get?Hello=Goodbye
可以發現,現在 url
已經被替換成了 https://httpbin.org/get?Hello=Goodbye
,可見 ro
會覆蓋 url
中的同名參數。
略顯遺憾的是,打印請求 URL 的代碼稍顯冗長:resp.RawResponse.Request.URL
,如果能夠像 requests
庫那樣提供 resp.Url
屬性直接返回請求的 url
值就更友好了。
POST 請求
發送 POST
請求更能體現 grequests
庫的方便之處,示例代碼如下:
// 創建一個要發送的數據結構
postData := map[string]string{
"username": "user",
"password": "pass",
}
// 將數據結構編碼爲 JSON 並準備請求選項
ro := &grequests.RequestOptions{
JSON: postData, // grequests 自動處理 JSON 編碼
// Data: postData,
}
// 發起 POST 請求
resp, _ := grequests.Post("https://httpbin.org/post", ro)
defer resp.Close()
// 輸出響應體內容
fmt.Println("Response:", resp.String())
這個示例代碼明顯比使用 net/http
發送 POST
請求的示例代碼整潔不少。
grequests
會根據 *grequests.RequestOptions
的屬性值,自動處理請求頭中的 Content-Type
。
你可以自行嘗試把 grequests.RequestOptions
中的 JSON
屬性改爲 Data
,即如下寫法:
ro := &grequests.RequestOptions{
Data: postData,
}
看看打印結果如何。
HTTP Basic Auth
在 requests
庫官方文檔首頁,有一個示例展示瞭如何使用 requests
設置 Basic Auth 認證,簡潔優雅,截圖如下:
現在我們嘗試用 grequests
來實現此功能:
ro := &grequests.RequestOptions{Auth: []string{"user", "pass"}}
resp, _ := grequests.Get("https://httpbin.org/basic-auth/user/pass", ro)
defer resp.Close()
if resp.Ok != true {
log.Println("Request did not return OK")
}
fmt.Println(resp.StatusCode)
fmt.Println(resp.Header.Get("content-type"))
fmt.Println(resp.String())
m := make(map[string]any)
_ = resp.JSON(&m)
fmt.Println(m)
可以發現,grequests
庫同樣能夠通過簡潔友好的方式來設置 Basic Auth 認證信息。
*grequests.Response
提供了 JSON
方法,可以將響應體的 JSON
信息反序列化到對象中。
相較於 requests
示例,grequests
唯一不足的是沒有提供 r.encoding
屬性可以方便獲取響應結果編碼格式。
上傳和下載文件
grequests
庫上傳和下載文件同樣非常簡單。
下載內容到文件:
resp, _ := grequests.Get("https://httpbin.org/get", nil)
defer resp.Close()
if resp.Ok != true {
log.Println("Request did not return OK")
}
// 下載響應體內容到 result.json
_ = resp.DownloadToFile("result.json")
*grequests.Response
提供了 DownloadToFile
方法可以直接將響應體內容寫入指定文件。
上傳文件內容到服務端:
// 從 result.json 讀取文件內容並上傳到服務端
fd, _ := grequests.FileUploadFromDisk("result.json")
// This will upload the file as a multipart mime request
resp, _ := grequests.Post("https://httpbin.org/post",
&grequests.RequestOptions{
Files: fd,
Data: map[string]string{"One": "Two"},
})
defer resp.Close()
if resp.Ok != true {
log.Println("Request did not return OK")
}
fmt.Println(resp)
上傳文件可以通過 *grequests.RequestOptions
的 Files
屬性來傳遞,並且可以同時使用 Data
參數傳遞表單數據(form
)。
總結
本文帶大家一起體驗了下 grequests
庫,展示了其強大之處。
相較於 Go 語言內置的 net/http
庫,grequests
的 API 設計更爲優雅易用,尤其從 POST
請求的對比中可以發現 grequests
的 API 要簡潔不少。
grequests
庫能夠以簡潔友好的方式設置 Basic Auth 認證信息。並且,上傳和下載文件同樣只需要很少的代碼即可實現。
這些強大之處的靈感,都來源於 Python 生態中的 requests
庫。
當然,grequests
也有一些小遺憾,它並沒有將 requests
庫的全部便捷功能複製過來。但總體而言,grequests
的出現讓我們在 Go 語言中發送 HTTP 請求方便了不少。
grequests
庫更多用法可以參考官方文檔。
本文示例源碼我都放在了 GitHub 中,歡迎點擊查看。
希望此文能對你有所啓發。
延伸閱讀
-
GRequests 源碼: https://github.com/levigross/grequests
-
GRequests 文檔: https://pkg.go.dev/github.com/levigross/grequests
-
requests 源碼 https://github.com/psf/requests
-
requests 文檔: https://requests.readthedocs.io/en/latest/
-
httpbin 官網: https://httpbin.org/
-
本文 GitHub 示例代碼:https://github.com/jianghushinian/blog-go-example/tree/main/grequests
聯繫我
-
公衆號:Go 編程世界
-
微信:jianghushinian
-
郵箱:jianghushinian007@outlook.com
-
博客:https://jianghushinian.cn
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/agJ2iCZBKtcJIm08UJqJtQ