返回信息流#### 背景
这两天尝试写个Go爬虫爬论坛,期望能登录后保存cookie,后续的访问都带着这个cookie。查看资料推荐用`net/http/cookiejar`。
目前能登录成功,获取成功登录Json。但是发现并未获取登录后的cookie,导致后续直接Get帖子正文报错**“您未登录,请登录后继续操作”**
请教各位大神,这种情况哪里出错了?
#### 实现
```
package main
import (
"net/http/cookiejar"
"net/url"
"strings"
"fmt"
"net/http"
"crypto/tls"
"io/ioutil"
)
func main() {
// init cookiejar
var cookieJar *cookiejar.Jar
cookieJar, _ = cookiejar.New(nil)
// init client with cookiejar
httpClient := &http.Client{
Jar: cookieJar,
}
// login param
postValues := url.Values{}
postValues.Set("id", "ID")
postValues.Set("passwd", "PWD")
postValues.Set("s-mode", "0")
postValues.Set("CookieDate", "3")
// request for login
httpReq, _ := http.NewRequest("POST", "https://bbs.byr.cn/user/ajax_login.json", strings.NewReader(postValues.Encode()))
httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
httpReq.Header.Add("X-Requested-With", "XMLHttpRequest")
httpReq.Header.Add("Connection", "keep-alive")
httpReq.Header.Add("User-Agent", "Mozilla/5.0")
httpReq.Header.Add("Referer", "https://bbs.byr.cn")
httpReq.Header.Add("Accept", "application/json, text/javascript, */*; q=0.01")
httpReq.Header.Add("authority", "bbs.byr.cn")
// for nginx/1.10
httpClient.Transport = &http.Transport{
TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
}
// login
httpResp, _ := httpClient.Do(httpReq)
fmt.Printf("req cookies: %s \n", httpReq.Cookies())
fmt.Printf("resp cookies: %s \n", httpResp.Cookies())
// request to get article content
httpReq1, _ := http.NewRequest("GET", "https://bbs.byr.cn/article/Python/2034", nil)
httpReq1.Header.Add("X-Requested-With", "XMLHttpRequest")
httpResp1, _ := httpClient.Do(httpReq1)
body, _ := ioutil.ReadAll(httpResp1.Body)
fmt.Println(string(body))
}
```
输出(可见cookie为空):
```
req cookies: []
resp cookies: []
(...省略...)
<h5>产生错误的可能原因:</h5><ul><li><samp class="ico-pos-dot"></samp>您未登录,请登录后继续操作</li>
(...省略...)
```
**困扰多时,求各位指点**
这是一条镜像帖。来源:北邮人论坛 / golang / #842同步于 2017/8/7
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Golang机器人发帖
【求助】Go爬虫无法获取论坛cookie的疑惑
buptlaogong
2017/8/7镜像同步8 回复
订阅后,新回复会通过你的通知中心匿名送达。
8 条回复
建议先使用抓包工具(推荐firefox下的tamper data配合firebug)抓包看一下, 然后确定请求时候的参数,以及预计的返回就可以写代码了
至于如何设置跟获取cookie, 这个很容易搜到: https://astaxie.gitbooks.io/build-web-application-with-golang/en/06.1.html
感谢回复!
我发帖之前看了些python版的爬虫问题帖,也进行抓包看了cookie内容,同时也查了很多Go http资料。资料大多推荐直接用cookiejar,https://golang.org/pkg/net/http/#CookieJar
但是,我的困惑在于【我的代码为什么无法获取登录后的cookie(如输出内容,cookie为空),这段代码哪里错在哪里】,大神能否用自己的论坛账号运行下这段代码,看看能否打印出cookie?万分感谢
【 在 nullne 的大作中提到: 】
: 建议先使用抓包工具(推荐firefox下的tamper data配合firebug)抓包看一下, 然后确定请求时候的参数,以及预计的返回就可以写代码了
: 至于如何设置跟获取cookie, 这个很容易搜到: https://astaxie.gitbooks.io/build-web-application-with-golang/en/06.1.html
:
: ...................
理论上不需要用session,看网上的示例,用cookieJar就能搞定,不知道为什么北邮人论坛的cookie无法获取
【 在 lucashood 的大作中提到: 】
: 不知道能不能在登录后弄一个session
: 我不会go
: 我瞎说的,python的requests模块有session api
: ...................
@buptlaogong 的确是个问题 我今天测试了一下 应该是Go自己的实现问题吧 或者说论坛的Cookie不规范 我待会儿再测试一下 先贴一点代码过来
所以简单的办法 就是你自己从header 里面解析 然后自己拼接cookie
package main
import (
"fmt"
"log"
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
"time"
"golang.org/x/net/publicsuffix"
)
func main() {
// Start a server to give us cookies.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if cookie, err := r.Cookie("Flavor"); err != nil {
// Illegal cookie name
http.SetCookie(w, &http.Cookie{Name: "map[Flavor]", Value: "Chocolate Chip"})
// Cannot process Expires and Value
http.SetCookie(w, &http.Cookie{Name: "map[Flavor]", Value: "Chocolate Chip", Path: "/", Expires: time.Now()})
// normal cookie
http.SetCookie(w, &http.Cookie{Name: "only", Value: "cookie"})
} else {
cookie.Value = "Oatmeal Raisin"
http.SetCookie(w, cookie)
}
}))
defer ts.Close()
u, err := url.Parse(ts.URL)
if err != nil {
log.Fatal(err)
}
// All users of cookiejar should import "golang.org/x/net/publicsuffix"
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Jar: jar,
}
resp, err := client.Get(u.String())
if err != nil {
log.Fatal(err)
}
// all cookies above will show in header "Set-Cookie"
fmt.Printf("header in request: %+v\n", resp.Header)
// only normal cookie will show here
fmt.Printf("cookies in request: %+v\n", resp.Cookies())
// only normal cookie will show here
fmt.Printf("cookies in jar: %+v\n", jar.Cookies(u))
}
【 在 nullne 的大作中提到: 】
: @buptlaogong 的确是个问题 我今天测试了一下 应该是Go自己的实现问题吧 或者说论坛的Cookie不规范 我待会儿再测试一下 先贴一点代码过来
: 所以简单的办法 就是你自己从header 里面解析 然后自己拼接cookie
: [code=c]
: ...................
貌似go解析Set-Cookie时认为 [ 是无效的字符,所以httpResp.Cookies()返回空,下面是把httpResp整个打印出来获取的Set-Cookie header:
```JSON
"Set-Cookie":[]string{
"nforum[UTMPUSERID]=guest;
path=/; domain=bbs.byr.cn",
"nforum[UTMPKEY]=21970208;
path=/; domain=bbs.byr.cn",
"nforum[UTMPNUM]=29282;
path=/; domain=bbs.byr.cn",
"nforum[UTMPUSERID]=guest;
path=/; domain=bbs.byr.cn",
"nforum[UTMPKEY]=21970208;
path=/; domain=bbs.byr.cn",
"nforum[UTMPNUM]=29282;
path=/; domain=bbs.byr.cn"}
```
下面是go解析cookie时依照的RFC标准: [http://tools.ietf.org/html/rfc6265](http://tools.ietf.org/html/rfc6265)
```
cookie-pair = cookie-name "=" cookie-value
cookie-name = token
cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
; US-ASCII characters excluding CTLs,
; whitespace DQUOTE, comma, semicolon,
; and backslash
token = 1*<any CHAR except CTLs or separators>
separators = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
```
标准里有如下规范:
```
token = 1*<any CHAR except CTLs or separators>
separators = "(" | ")" | "<" | ">" | "@"
| "," | ";" | ":" | "\" | <">
| "/" | "[" | "]" | "?" | "="
| "{" | "}" | SP | HT
```
也就是说,token不能包含分隔符,但是`"[" "]"`又恰好属于分隔符,所以被判为无效