【Go 入门学习】第一篇关于 Go 的博客--Go 爬虫初体验
一、写在前面
其实早就该写这一篇博客了,为什么一直没有写呢?还不是因为忙不过来(实际上只是因为太懒了)。不过好了,现在终于要开始写这一篇博客了。在看这篇博客之前,可能需要你对 go 这门语言有些基本的了解,比如基础语法之类的。话不多说,进入正题。
二、go 环境配置
1.安装配置
在学习一门语言时,第一步就是环境配置了,go 也不例外,下面就是 windows 下 go 开发环境的配置过程了。
首先你需要下载 go 的安装包,可以打开 go 语言中文网下载,地址为:。
下载完成后打开安装(例如安装到 e:\go 目录),然后配置环境变量,将安装目录下的 bin 目录路径加入环境变量中。这一步完成后打开命令行,输入 go version,若出现版本信息则表明配置成功。
2.配置 gopath 和 goroot
除了要将 bin 目录加入到环境变量中,还要配置 gopath 和 goroot,步骤如下:
在用户变量中新建变量 gopath:
在系统变量中新建变量 goroot:
3. 选择 ide
在 ide 的选择上,我比较推荐使用 jetbrains 家的 goland,功能强大,使用起来也很方便。
三、下载网页
下载网页使用的是 go 中原生的 http 库,在使用前需要导包,和 python 一样用 import 导入即可。如果要发送 get 请求,可以直接使用 http 中的 get() 方法,例如:
1 package main 2 3 import ( 4 "fmt" 5 "net/http" 6 ) 7 8 func main () { 9 html, err := http.get("https://www.baidu.com/") 10 if err != nil { 11 fmt.println(err) 12 } 13 fmt.println(html) 14 }
get() 方法有两个返回值,html 表示请求的结果,err 表示错误,这里必须对 err 做判断,go 语言的错误机制就是这样,这里不多做解释。
这么用起来确实很简单,但是不能自己设置请求头,如果要构造请求头的话可以参考下面的例子:
1 req, _ := http.newrequest("get", url, nil) 2 // set user-agent 3 req.header.add("useragent", "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/78.0.3904.97 safari/537.36") 4 client := &http.client{} 5 resp, err := client.do(req)
四、解析网页
1.解析库选择
go 语言中可以用来解析网页的库也是很齐全的,xpath、css 选择器和正则表达式都能用。这里我用的是 htmlquery,这个库使用的是 xpath 选择器。htmlquery 是用于 html 的 xpath 数据提取库,可通过 xpath 表达式从 html 文档中提取数据。xpath 语法就不提了,毕竟用 python 写爬虫的时候没少用。
先说下 htmlquery 的安装吧,一般会推荐你使用如下命令安装:
go get github.com/antchfx/htmlquery
但是你懂的,出于某些原因就下载不下来,怎么办呢?对于这种能在 github 上找到的库直接 clone 到本地就行了,记得要复制到你的 goapth 下。
2.使用 htmlquery
在使用 htmlquery 这个库的时候,可能会报错说缺少 golang.org\x\text,和上面的解决办法一样,去 github 上找,然后 clone 下来。
下面是 htmlquery 中经常使用的方法及相应含义:
func parse(r io.reader) (*html.node, error)
: 返回给定 reader 的 html 的解析树。
func find(top *html.node, expr string) []*html.node
: 搜索与指定 xpath 表达式匹配的 html.node。
func findone(top *html.node, expr string) *html.node
: 搜索与指定 xpath 表达式匹配的 html.node,并返回匹配的第一个元素,可简单理解为findone = find[0]
。
func innertext(n *html.node) string
: 返回对象的开始和结束标记之间的文本。
func selectattr(n *html.node, name string) (val string)
: 返回指定名称的属性值。
func outputhtml(n *html.node, self bool) string
: 返回包含标签名称的文本。
下面是使用 htmlquery 解析网页的代码:
1 // used to parse html 2 func parse(html string) { 3 // parse html 4 root, _ := htmlquery.parse(strings.newreader(html)) 5 titlelist := htmlquery.find(root, `//*[@id="post_list"]/div/div[2]/h3/a/text()`) 6 hreflist := htmlquery.find(root, `//*[@id="post_list"]/div/div[2]/h3/a/@href`) 7 authorlist := htmlquery.find(root, `//*[@id="post_list"]/div/div[2]/div/a/text()`) 8 9 // traverse the result 10 for i := range titlelist { 11 blog := bloginfo{} 12 blog.title = htmlquery.innertext(titlelist[i]) 13 blog.href = htmlquery.innertext(hreflist[i]) 14 blog.author = htmlquery.innertext(authorlist[i]) 15 fmt.println(blog) 16 } 17 }
需要注意的是由于在 go 语言中不支持使用单引号来表示字符串,而要使用反引号“`”和双引号来表示字符串。然后因为 find() 方法返回的是一个数组,因而需要遍历其中每一个元素,使用 for 循环遍历即可。在 for 循环中使用到的 bloginfo 是一个结构体,表示一个博客的基本信息,定义如下:
1 // used to record blog information 2 type bloginfo struct { 3 title string 4 href string 5 author string 6 }
五、go 并发
在 go 语言中使用 go 关键字开启一个新的 go 程,也叫 goroutine,开启成功之后,go 关键字后的函数就将在开启的 goroutine 中运行,并不会阻塞当前进程的执行,所以要用 go 来写并发还是很容易的。例如:
1 baseurl := "https://www.cnblogs.com/" 2 for i := 2; i < 4; i ++ { 3 url := baseurl + "#p" + strconv.itoa(i) 4 // fmt.println(url) 5 go request(url) 6 } 7 8 // wait for goroutine 9 time.sleep(2 * time.second) 10 request(baseurl)
这里除了在主进程中有一个 request(),还开启了两个 go 程来执行 request()。不过要注意的是,一旦主进程结束,其余 go 程也会结束,所以我这里加了一个两秒钟的等待时间,用于让 go 程先结束。
六、体验总结
由于我本身才刚开始学习 go,就还有很多东西没有学到,所以这个初体验其实还有很多没写到的地方,比如数据保存,去重问题等等,后面会去多看看 go 的官方文档。当然了,对我来说,要写爬虫的话还是会用 python 来写的,不过还是得花时间学习新知识,比如使用 go 做开发,熟悉掌握 go 语言就是我的下一目标了。
完整代码已上传到 github!