使用 Golang 写爬虫经验总结

模拟登录

模拟登录最重要的是保存cookies的状态,例如在填写验证码的页面,服务器会传给客户端一个sessionID保存在cookies中,客户端在提交用户账户和验证码等信息时,需要连同这个cookies一起提交,否则服务器就无法判断两次请求是否为同一个客户端,从而导致验证码验证失败。
在Golang中可以使用CookieJar管理cookies,在创建http.Client的对象时,传入一个非空的CookieJar对象即可。设置了之后,Golang在收到服务器的响应之后,会自动把响应头中的cookies信息保存到CookieJar中,在下次发起请求时,自动从CookieJar中取出cookies信息放到请求头中。

// &cookiejar.Options{PublicSuffixList: publicsuffix.List},这是为了可以根据域名安全地设置cookies
cookieJar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
   panic(err)
}
client = http.Client{Jar: cookieJar, Timeout: time.Second * 3}

POST提交

GET提交方式很简单,直接拼接字符串就行了。POST提交需要一个可读的(io.Reader)请求体body,body中是形如a=b&c=d格式的查询字符串(字节切片)。可以借助url.Values方便的生成查询字符串,它本质是一个map[string][]string,提供了SetAdd等方法,让操作这个map更简单。

params := url.Values{}
// 添加参数
params.Add("memberAccount", "xxx")
// 添加sha1后的参数
params.Add("memberUmm", fmt.Sprintf("%x", sha1.Sum([]byte("xxxx"))))
params.Add("check", captcha)
params.Add("rememberMe", "on")
// 1.必须设置正确的Content-Type,否则服务器无法正确识别参数
// 2.params.Encode()生成查询字符串,然后用string.NewReader包裹这个字符串使其可读
res, err := client.Post("https://www.example.com/login.json", "application/x-www-form-urlencoded", strings.NewReader(params.Encode()))
if err != nil {
   return "", err
}
// 记得关闭
defer res.Body.Close()

上传文件

上传文件需要借助multipart.Writer向请求体中写入相应的数据,使用multipart.NewWriter生成这样的一个写入器,它接收一个io.Writer作参数,这个参数即我们的表单体bodybody除了需要可写,还要可读(让httpClient读取参数发送到服务器),并且它是流式的,所以选用bytes.Buffer,一个可读写大小可变的字节流缓冲器。

body := new(bytes.Buffer)
// 创建body的写入器
mulWriter := multipart.NewWriter(body)

写入普通字段

// 直接调用writeField方法即可,参数1是参数名,参数2是参数值
err := mulWriter.WriteField("user_name", "xxx")
if err != nil {
    return "", err
}

写入文件字段

要写入文件,需要先调用CreateFormFile创建一个文件内容写入器,再通过写入器写入文件的内容

// 创建文件写入器,并指明文件参数名和参数值
fileWriter, err := mulWriter.CreateFormFile("upload", filepath.Base(filename))
if err != nil {
    return "", err
}
// 打开需要上传的文件
file, err := os.Open(filename)
if err != nil {
    return "", err
}
defer file.Close()
// 复制文件内容到写入器
_, err = io.Copy(fileWriter, file)
if err != nil {
    return "", err
}
// 记得关闭,让缓冲区的内容写入body中
err = mulWriter.Close()
if err != nil {
   return "", err
}
res, err := client.Post("http://v1-http-api.jsdama.com/api.php?mod=php&act=upload", mulWriter.FormDataContentType(), body)

上面例子的最后一行,必须使用FormDataContentType()方法获取正确的Content-Type,而不能自己写multipart/form-data,这是因为boundary是随机生成的,这个必须由Golang告诉我们正确的值。boundary即表单体中分隔每个参数的一个标志位,如下图:

使用 Golang 写爬虫经验总结

使用Fiddler调试程序

使用Fiddler抓包可以让我们方便的看到程序提交到服务器的数据格式,使得调试和修改程序更加简单。Fiddler相当于一个正向代理服务器,启动后,它会把IE的代理服务器设置为http://127.0.0.1:8888 ,即Fiddler默认的代理地址,这样所有浏览器的请求都会先通过Fiddler,再由Fiddler转发出去,实现抓包。
但是上面的机制只对系统的浏览器有效,要对其他程序也生效,需要单独的设置程序的代理。
Golang设置代理比较简单,只需要增加一个环境变量设置即可,可以修改系统的环境变量,也可以通过代码动态添加。

// 设置httpClient的代理
os.Setenv("HTTP_PROXY", "http://127.0.0.1:8888")

解决HTTPS解密失败的问题

如果Fiddler出现无法解密HTTPS请求,看不到原始请求数据的情况,可以尝试重新安装Fiddler的根证书来解决。具体操作:

  1. 打开Fiddler,依次点击菜单Tools->Options,打开设置对话框,点击选中"HTTPS"面板

使用 Golang 写爬虫经验总结

  1. 取消“Decrypt HTTPS traffic”的选中状态,点击“Actions”按钮,点击“Reset All Certificates”,之后会弹出确认框问你是否要删除当前的证书并创建新的证书,一路允许即可

使用 Golang 写爬虫经验总结

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!