其实早就该写这一篇博客了,为何一直没有写呢?还不是由于忙不过来(实际上只是由于太懒了)。不过好了,如今终于要开始写这一篇博客了。在看这篇博客以前,可能须要你对 Go 这门语言有些基本的了解,好比基础语法之类的。话很少说,进入正题。html
在学习一门语言时,第一步就是环境配置了,Go 也不例外,下面就是 Windows 下 Go 开发环境的配置过程了。git
首先你须要下载 Go 的安装包,能够打开 Go 语言中文网下载,地址为:https://studygolang.com/dl。github
下载完成后打开安装(例如安装到 E:\Go 目录),而后配置环境变量,将安装目录下的 bin 目录路径加入环境变量中。这一步完成后打开命令行,输入 go version,若出现版本信息则代表配置成功。golang
除了要将 bin 目录加入到环境变量中,还要配置 GOPATH 和 GOROOT,步骤以下:正则表达式
在用户变量中新建变量 GOPATH:数组
在系统变量中新建变量 GOROOT:并发
在 IDE 的选择上,我比较推荐使用 jetbrains 家的 GoLand,功能强大,使用起来也很方便。函数
下载网页使用的是 Go 中原生的 http 库,在使用前须要导包,和 Python 同样用 import 导入便可。若是要发送 GET 请求,能够直接使用 http 中的 Get() 方法,例如:post
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)
Go 语言中能够用来解析网页的库也是很齐全的,XPath、CSS 选择器和正则表达式都能用。这里我用的是 htmlquery,这个库使用的是 Xpath 选择器。htmlquery 是用于 HTML 的 XPath 数据提取库,可经过 XPath 表达式从 HTML 文档中提取数据。Xpath 语法就不提了,毕竟用 Python 写爬虫的时候没少用。
先说下 htmlquery 的安装吧,通常会推荐你使用以下命令安装:
go get github.com/antchfx/htmlquery
可是你懂的,出于某些缘由就下载不下来,怎么办呢?对于这种能在 GitHub 上找到的库直接 clone 到本地就好了,记得要复制到你的 GOAPTH 下。
在使用 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 程,也叫 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!