【背景】
折腾:
期间,需要去发送POST操作,去模拟登陆百度,且post时要传递对应的post数据。
【折腾过程】
1.自己参考官网:
http://golang.org/pkg/net/http/
去看看,POST的参数和NewRequest的参数。
2.期间想要实现go中的函数的可选参数,默认参数,结果不支持:
3.后来看了半天,很多帖子,貌似都是那个Form或PostForm的,但是感觉不太对。
4.参考:
看到是http.NewRequest设置为POST时,还传递了对应的postData了。
然后就去看看:
bytes.NewReader
的输入参数,是什么类型的。
http://golang.org/pkg/bytes/#NewReader
然后发现,就是普通的,byte[]
5.所以到目前为止,貌似明白了:
对于POST时所要传递的post data
是需要:
在http.NewRequest
http://golang.org/pkg/net/http/#NewRequest
时传入给:
body io.Reader |
的,
但是:
http://golang.org/pkg/io/#Reader
不会用。
而别人传入的:
http://golang.org/pkg/bytes/#NewReader
还是基本能看懂的:
就是一个普通的reader,然后输入的是[]byte
而这个[]byte
是post data这string,被转换为对应的[]byte
而对应的post data的string,是key=value,中间通过&分隔开的。
需要自己,对于输入的postDict的键值对,自己组合出来。
或者是:
go中,或许也有对应的库函数去实现encode。
6.而之前看到:
http://golang.org/pkg/net/http/httputil/
看看有没有这样的工具。
不过貌似没看到。
但是看到其他的东西:
http://golang.org/pkg/net/http/httputil/#DumpRequest
可以帮你打印出来request,供调试用。
7.看到:
How to make an HTTP POST request in many languages
想到了:
go中,是不会用另外的url,去实现类似于
C#中的HttpUtility.UrlEncode?
所以去看看url:
http://golang.org/pkg/net/url/
然后终于找到所要的了:
http://golang.org/pkg/net/url/#Values.Encode
func (Values) Encodefunc (v Values) Encode() string Encode encodes the values into “URL encoded” form. e.g. "foo=bar&bar=baz" |
8.然后就可以去尝试写POST相关代码了。
然后是用下面核心代码:
/*
* [File]
* EmulateLoginBaidu.go
*
* [Function]
* 【记录】用go语言实现模拟登陆百度
* https://www.crifan.com/emulate_login_baidu_using_go_language/
*
* [Version]
* 2013-09-21
*
* [Contact]
* https://www.crifan.com/about/me/
*/
package main
import (
"fmt"
"os"
"runtime"
"path"
"strings"
"time"
//"io"
"io/ioutil"
"net/http"
"net/http/cookiejar"
"net/url"
"bytes"
)
/***************************************************************************************************
Global Variables
***************************************************************************************************/
var gCurCookies []*http.Cookie;
var gCurCookieJar *cookiejar.Jar;
var gLogger log4go.Logger;
/***************************************************************************************************
Functions
***************************************************************************************************/
//do init before all others
func initAll(){
gCurCookies = nil
//var err error;
gCurCookieJar,_ = cookiejar.New(nil)
gLogger = nil
//......
}
//get url response html
func getUrlRespHtml(strUrl string, postDict map[string]string) string{
gLogger.Info("getUrlRespHtml, strUrl=%s", strUrl)
gLogger.Info("postDict=%s", postDict)
var respHtml string = "";
httpClient := &http.Client{
//Transport:nil,
//CheckRedirect: nil,
Jar:gCurCookieJar,
}
var httpReq *http.Request
//var newReqErr error
if nil == postDict {
gLogger.Info("is GET")
//httpReq, newReqErr = http.NewRequest("GET", strUrl, nil)
httpReq, _ = http.NewRequest("GET", strUrl, nil)
// ...
//httpReq.Header.Add("If-None-Match", `W/"wyzzy"`)
} else {
gLogger.Info("is POST")
postValues := url.Values{}
for postKey, PostValue := range postDict{
postValues.Set(postKey, PostValue)
}
gLogger.Info("postValues=%s", postValues)
postDataStr := postValues.Encode()
gLogger.Info("postDataStr=%s", postDataStr)
postDataBytes := []byte(postDataStr)
gLogger.Info("postDataBytes=%s", postDataBytes)
postBytesReader := bytes.NewReader(postDataBytes)
//httpReq, newReqErr = http.NewRequest("POST", strUrl, postBytesReader)
httpReq, _ = http.NewRequest("POST", strUrl, postBytesReader)
//httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
}
httpResp, err := httpClient.Do(httpReq)
// ...
//httpResp, err := http.Get(strUrl)
//gLogger.Info("http.Get done")
if err != nil {
gLogger.Warn("http get strUrl=%s response error=%s\n", strUrl, err.Error())
}
gLogger.Info("httpResp.Header=%s", httpResp.Header)
gLogger.Debug("httpResp.Status=%s", httpResp.Status)
defer httpResp.Body.Close()
// gLogger.Info("defer httpResp.Body.Close done")
body, errReadAll := ioutil.ReadAll(httpResp.Body)
//gLogger.Info("ioutil.ReadAll done")
if errReadAll != nil {
gLogger.Warn("get response for strUrl=%s got error=%s\n", strUrl, errReadAll.Error())
}
//gLogger.Debug("body=%s\n", body)
//gCurCookies = httpResp.Cookies()
//gCurCookieJar = httpClient.Jar;
gCurCookies = gCurCookieJar.Cookies(httpReq.URL);
//gLogger.Info("httpResp.Cookies done")
//respHtml = "just for test log ok or not"
respHtml = string(body)
//gLogger.Info("httpResp body []byte to string done")
return respHtml
}
func dbgPrintCurCookies() {
var cookieNum int = len(gCurCookies);
gLogger.Info("cookieNum=%d", cookieNum)
for i := 0; i < cookieNum; i++ {
var curCk *http.Cookie = gCurCookies[i];
//gLogger.Info("curCk.Raw=%s", curCk.Raw)
gLogger.Info("------ Cookie [%d]------", i)
gLogger.Info("Name\t\t=%s", curCk.Name)
gLogger.Info("Value\t=%s", curCk.Value)
gLogger.Info("Path\t\t=%s", curCk.Path)
gLogger.Info("Domain\t=%s", curCk.Domain)
gLogger.Info("Expires\t=%s", curCk.Expires)
gLogger.Info("RawExpires\t=%s", curCk.RawExpires)
gLogger.Info("MaxAge\t=%d", curCk.MaxAge)
gLogger.Info("Secure\t=%t", curCk.Secure)
gLogger.Info("HttpOnly\t=%t", curCk.HttpOnly)
gLogger.Info("Raw\t\t=%s", curCk.Raw)
gLogger.Info("Unparsed\t=%s", curCk.Unparsed)
}
}
func main() {
initAll()
//......
//step3: verify returned cookies
if bGotCookieBaiduid && bExtractTokenValueOK {
gLogger.Info("======步骤3:登陆百度并检验返回的Cookie ======");
staticPageUrl := "http://www.baidu.com/cache/user/html/jump.html";
postDict := map[string]string{}
//postDict["ppui_logintime"] = ""
postDict["charset"] = "utf-8"
//postDict["codestring"] = ""
postDict["token"] = strLoginToken
postDict["isPhone"] = "false"
postDict["index"] = "0"
//postDict["u"] = ""
//postDict["safeflg"] = "0"
postDict["staticpage"] = staticPageUrl
postDict["loginType"] = "1"
postDict["tpl"] = "mn"
postDict["callback"] = "parent.bdPass.api.login._postCallback"
strBaiduUsername := ""
strBaiduPassword := ""
// stdinReader := bufio.NewReader(os.Stdin)
// inputBytes, _ := stdinReader.ReadString('\n')
// fmt.Printf("Input Char Is : %v", string([]byte(input)[0]))
//_, err1 := fmt.Scanf("%s", &strBaiduUsername)
//fmt.Println("Plese input:")
//fmt.Println("Baidu Username:")
gLogger.Info("Plese input:")
gLogger.Info("Baidu Username:")
_, err1 := fmt.Scanln(&strBaiduUsername)
if nil == err1 {
gLogger.Info("strBaiduUsername=%s", strBaiduUsername)
}
//fmt.Println("Baidu Password:")
gLogger.Info("Baidu Password:")
//_, err2 := fmt.Scanf("%s", &strBaiduPassword)
_, err2 := fmt.Scanln(&strBaiduPassword)
if nil == err2 {
gLogger.Info("strBaiduPassword=%s", strBaiduPassword)
}
postDict["username"] = strBaiduUsername
postDict["password"] = strBaiduPassword
postDict["verifycode"] = ""
postDict["mem_pass"] = "on"
gLogger.Debug("postDict=%s", postDict)
baiduMainLoginUrl := "https://passport.baidu.com/v2/api/?login";
loginBaiduRespHtml := getUrlRespHtml(baiduMainLoginUrl, postDict);
gLogger.Debug("loginBaiduRespHtml=%s", loginBaiduRespHtml)
}
}实现了可以成功发送POST,传递进入post data:
[2013/09/21 18:26:42 ] [INFO] (main.main:294) ======步骤3:登陆百度并检验返回的Cookie ====== [2013/09/21 18:26:42 ] [INFO] (main.main:319) Plese input: [2013/09/21 18:26:42 ] [INFO] (main.main:320) Baidu Username: [2013/09/21 18:26:48 ] [INFO] (main.main:323) strBaiduUsername=xxxxxx [2013/09/21 18:26:48 ] [INFO] (main.main:326) Baidu Password: [2013/09/21 18:26:50 ] [INFO] (main.main:330) strBaiduPassword=yyyyyy [2013/09/21 18:26:50 ] [DEBG] (main.main:338) postDict=map[charset:utf-8 isPhone:false index:0 tpl:mn username:xxxxxx verifycode: token:0933758100af2943e1948ea011386ac8 staticpage:http://www.baidu.com/cache/user/html/jump.html loginType:1 callback:parent.bdPass.api.login._postCallback password:yyyyyy mem_pass:on] [2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:127) getUrlRespHtml, strUrl=https://passport.baidu.com/v2/api/?login [2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:128) postDict=map[token:0933758100af2943e1948ea011386ac8 staticpage:http://www.baidu.com/cache/user/html/jump.html loginType:1 callback:parent.bdPass.api.login._postCallback password:yyyyyy mem_pass:on charset:utf-8 isPhone:false index:0 tpl:mn username:xxxxxx verifycode:] [2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:147) is POST [2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:152) postValues=map[token:[0933758100af2943e1948ea011386ac8] staticpage:[http://www.baidu.com/cache/user/html/jump.html] loginType:[1] callback:[parent.bdPass.api.login._postCallback] password:[yyyyyy] mem_pass:[on] charset:[utf-8] isPhone:[false] index:[0] tpl:[mn] username:[xxxxxx] verifycode:[]] [2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:154) postDataStr=callback=parent.bdPass.api.login._postCallback&charset=utf-8&index=0&isPhone=false&loginType=1&mem_pass=on&password=yyyyyy&staticpage=http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fjump.html&token=0933758100af2943e1948ea011386ac8&tpl=mn&username=xxxxxx&verifycode= [2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:156) postDataBytes=callback=parent.bdPass.api.login._postCallback&charset=utf-8&index=0&isPhone=false&loginType=1&mem_pass=on&password=yyyyyy&staticpage=http%3A%2F%2Fwww.baidu.com%2Fcache%2Fuser%2Fhtml%2Fjump.html&token=0933758100af2943e1948ea011386ac8&tpl=mn&username=xxxxxx&verifycode=
然后模拟登陆百度,可以正常返回对应的各种cookie的:
[2013/09/21 18:26:50 ] [INFO] (main.getUrlRespHtml:172) httpResp.Header=map[Etag:[w/"Zv4yMMLJASqKOV17EWzXUpi3rNVEPvgZ:1379759203"] Set-Cookie:[BDUSS=G1LNG5uLTNYWkU2bzA2SGxCZHZ2Rm5ocnN-MEhFem5uQkZrdkJFVmplUmpBV1ZTQVFBQUFBJCQAAAAAAAAAAAEAAAB-OUgCYWdhaW5pbnB1dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGN0PVJjdD1SM; expires=Wed, 08-Dec-2021 10:26:43 GMT; path=/; domain=baidu.com; httponly PTOKEN=deleted; expires=Fri, 21-Sep-2012 10:26:42 GMT; path=/; domain=baidu.com; httponly PTOKEN=0f1e0187b042630a47c4eea8e0e96a2f; expires=Wed, 08-Dec-2021 10:26:43 GMT; path=/; domain=passport.baidu.com; httponly STOKEN=8d6ce0cbc7f689a8cd647b8beb5872e3; expires=Wed, 08-Dec-2021 10:26:43 GMT; path=/; domain=passport.baidu.com; httponly SAVEUSERID=deleted; expires=Fri, 21-Sep-2012 10:26:42 GMT; path=/; domain=passport.baidu.com; httponly USERNAMETYPE=1; expires=Wed, 08-Dec-2021 10:26:43 GMT; path=/; domain=passport.baidu.com; httponly] Cache-Control:[public] Server:[] P3p:[CP=" OTI DSP COR IVA OUR IND COM "] Date:[Sat, 21 Sep 2013 10:26:43 GMT] Content-Type:[text/html] Connection:[keep-alive] Last-Modified:[Sat, 21 Sep 2013 10:26:43 10SepGMT] Pragma:[public] Expires:[0] Vary:[Accept-Encoding]]
[2013/09/21 18:26:50 ] [DEBG] (main.getUrlRespHtml:173) httpResp.Status=200 OK
[2013/09/21 18:26:50 ] [DEBG] (main.main:342) loginBaiduRespHtml=<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<script type="text/javascript">
var url = encodeURI('http://www.baidu.com/cache/user/html/jump.html?hao123Param=RzFMTkc1dUxUTllXa1UyYnpBMlNHeENaSFoyUm01b2NuTi1NRWhGZW01dVFrWnJka0pGVm1wbFVtcEJWMVpUUVZGQlFVRkJKQ1FBQUFBQUFBQUFBQUVBQUFCLU9VZ0NZV2RoYVc1cGJuQjFkQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBR04wUFZKamREMVNN&callback=parent.bdPass.api.login._postCallback&index=0&codestring=&username=xxxxxx&phonenumber=&mail=&tpl=mn&u=https%3A%2F%2Fpassport.baidu.com%2F&needToModifyPassword=0&gotourl=&auth=&error=0');
//parent.callback(url)
window.location.replace(url);
</script>
</body>
</html>如图:
至此,至少基本的POST,是搞定了。
【总结】
此处,对于http的POST,且要传递对应的post data的话,最最核心的代码是:
import (
"net/http"
"net/url"
"bytes"
)
//get url response html
func getUrlRespHtml(strUrl string, postDict map[string]string) string{
//......
httpClient := &http.Client{
//Transport:nil,
//CheckRedirect: nil,
Jar:gCurCookieJar,
}
var httpReq *http.Request
//var newReqErr error
if nil == postDict {
gLogger.Info("is GET")
//httpReq, newReqErr = http.NewRequest("GET", strUrl, nil)
httpReq, _ = http.NewRequest("GET", strUrl, nil)
// ...
//httpReq.Header.Add("If-None-Match", `W/"wyzzy"`)
} else {
gLogger.Info("is POST")
postValues := url.Values{}
for postKey, PostValue := range postDict{
postValues.Set(postKey, PostValue)
}
gLogger.Info("postValues=%s", postValues)
postDataStr := postValues.Encode()
gLogger.Info("postDataStr=%s", postDataStr)
postDataBytes := []byte(postDataStr)
gLogger.Info("postDataBytes=%s", postDataBytes)
postBytesReader := bytes.NewReader(postDataBytes)
//httpReq, newReqErr = http.NewRequest("POST", strUrl, postBytesReader)
httpReq, _ = http.NewRequest("POST", strUrl, postBytesReader)
//httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
httpReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
}
httpResp, err := httpClient.Do(httpReq)
//......
}
func main() {
//......
postDict := map[string]string{}
postDict["charset"] = "utf-8"
postDict["token"] = strLoginToken
postDict["isPhone"] = "false"
postDict["index"] = "0"
//......
baiduMainLoginUrl := "https://passport.baidu.com/v2/api/?login";
loginBaiduRespHtml := getUrlRespHtml(baiduMainLoginUrl, postDict);
gLogger.Debug("loginBaiduRespHtml=%s", loginBaiduRespHtml)
//......
}即可。