从零开始Golang爬虫(一)| 预备知识
这几天我这学期选修的实训课开课了,实践内容是用python的scrapy框架实现爬虫,最后还要做数据展示。因为框架屏蔽了许多细节,不便于理解原理,而python又不熟悉,所以决定自己用Go原生实现一下。
首先需要掌握爬虫的预备知识,主要包括正则表达式、CSS选择器或XPath,可以通过它们从页面中提取有效信息,这些在菜鸟教程都可以查到,所以这里只列一些常用的规则以及如何在Go中调用。
正则表达式
字符 描述
^ 以^后面字符开头
. 任意字符
* *前面字符出现任意次
$ 以$前面字符结尾
? 非贪婪匹配(从前向后)
+ +前面字符出现至少一次
{2} 前面字符出现2次
{2,} 前面字符出现最少2次
{2,5} 前面字符出现2-5次
| 或
[abcd] 满足abcd任意一个
[A-Za-z0-9_] 满足区间内任意一个
[^1] 满足除1以外任意字符
\s [\t\n\f\r ]
\S 与\s相反
\w [A-Za-z0-9_]
\W 与\w相反
[\u4E00-\u9FA5] 汉字
\d 数字
regexp
Go标准库提供了专门的正则表达式库regexp,采用RE2语法,与python正则基本一致
regexp包的内容很简单,主要由Match、Compile、Find和ReplaceAll四部分组成,在爬虫中ReplaceAll一般用不到
Match
首先是以Match开头的3个函数:
func Match(pattern string, b []byte) (matched bool, err error)
func MatchString(pattern string, s string) (matched bool, err error)
func MatchReader(pattern string, r io.RuneReader) (matched bool, err error)
都用于检查是否存在匹配pattern的子序列,区别仅在于待匹配串的数据类型不同
Compile
Compile用于将模式串转换成Regexp对象,然后可以通过Regexp对象进行Match与Find
func Compile(expr string) (*Regexp, error)
func MustCompile(str string) *Regexp
func CompilePOSIX(expr string) (*Regexp, error)
func MustCompilePOSIX(str string) *Regexp
Must表示解析失败会直接panic,POSIX会将匹配模式设置为leftmost-longest,这里一般只用MustCompile就好
Find
Find包括十多种方法,都由以下四种参数排列组合而成:
参数 | 描述 |
---|---|
String | 传入String,没有则传入[]byte |
All | 返回所有结果,没有则只返回第一个 |
Submatch | 返回整串匹配以及所有子匹配的Silce |
Index | 返回下标,没有则返回匹配到的字符串 |
CSS选择器
字符 | 描述 |
---|---|
* | 选择所有节点 |
#container | 选择id为container的节点 |
.container | 选择所有class包含container的节点 |
li a | 选择所有li下的所有a节点 |
ul + p | 选择ul后面的第一个p元素 |
div#container > ul | 选取id为container的div的第一个ul子元素 |
ul ~ p | 选取与ul相邻的所有p元素 |
a[title] | 选取所有有title属性的a元素 |
a[href=http://baidu.com] | 选取所有href属性为baidu.com的a元素 |
a[href*=baidu] | 选取所有href属性包含baidu的a元素 |
a[href^=http] | 选取所有href属性值以http开头的a元素 |
a[href$=.jpg] | 选取所有href属性值以.jpg结尾的a元素 |
input[type=radio]:checked | 选择选中的radio元素 |
div:not(#container) | 选取所有id非container的div元素 |
li:nth-child(3) | 选取第三个li元素 |
tr:nth-child(2n) | 选取第偶数个tr元素 |
goquery
goquery是github上的开源库,语法类似jQuery,除了提供CSS选择器还支持contains和has这样的内容过滤器,还能轻松地实现dom节点操作
doc, err := goquery.NewDocumentFromReader(body)
if err != nil {
log.Fatalf("Parse error: %s", err)
}
NewDocumentFromReader函数接收一个io.Reader接口变量,返回Document指针,然后可以通过doc.Find基于CSS选择器查找,返回Selection指针表示html中的一个node,
而对于* Selection,goquery提供了诸如Next、Parent、Children、Each、Map等大量方法实现了节点操作,同样也可以通过Find进一步查找。
XPath
字符 | 描述 |
---|---|
article | 选取所有article元素的所有子节点 |
/article | 选取根元素article |
article/a | 选取所有属于article的子元素的a元素 |
//div | 选取所有div子元素 |
article//div | 选取所有属于article元素的后代的div元素 |
//@class | 选取所有名为class的属性 |
/article/div[1] | 选取属于article子元素的第一个div元素 |
/article/div[last()] | 选取属于article子元素的最后一个div元素 |
/article/div[last()-1] | 选取属于article子元素的倒数第二个div元素 |
//div[@lang] | 选取所有拥有lang属性的div元素 |
//div[@lang=eng] | 选取所有lang属性为eng的div元素 |
/div/* | 选取属于div元素的所有子节点 |
//* | 选取所有元素 |
//div[@*] | 选取所有带属性的title元素 |
/div/a | //div/p | 选取所有div元素的a和p元素 |
//span | //ul | 选取文档中的span和ul元素 |
article/div/p | //span | 选取所有属于article元素的div元素的p元素以及文档中所有的span元素 |
小结
至此,爬虫的预备知识就全部结束了,由于我对于CSS比较熟悉,所以将主要使用CSS选择器配合正则表达式进行数据的解析。